import time
import traceback
from warnings import simplefilter
import re
from sc_code.envs.utils.geo import get_two_point_distance, convert_lua_obj_to_dict

simplefilter(action='ignore', category=FutureWarning)
from ray.rllib.env.multi_agent_env import MultiAgentEnv
from sc_code.envs.env import Environment
from sc_code.envs import etc02 as etc
from sc_code.envs.common.utils import MidPointCoordinate
from sc_code.envs.utils.geo import get_two_point_distance
from envs.utils.geo import get_point_with_point_bearing_distance
from ray.remote_handle_docker import restart_mozi_container

MAX_DOCKER_RETRIES = 3


class Env(MultiAgentEnv):
    def __init__(self, env_config):
        self.steps = 0
        self.env_config = env_config
        self.reset_nums = 0
        self._get_env()
        self.side_name = env_config['side_name']
        self.scenario = self.env.reset(self.side_name)
        self.NauticalMiles_to_Kilometers = 1.852

    def _init_data_structure(self):
        self.anti_ships_aircraft_Group01 = ['F-16A #1', 'F-16A #2', 'F-16A #3', 'F-16A #4', 'F-16A #5', 'F-16A #6']
        self.anti_ships_aircraft_Group02 = ['F-16A #01', 'F-16A #02', 'F-16A #03', 'F-16A #04', 'F-16A #05',
                                            'F-16A #06']
        self.air_air_aircraft_Group01 = ['F-16A #7', 'F-16A #8', 'F-16A #9']
        self.air_air_aircraft_Group02 = ['F-16A #07', 'F-16A #08', 'F-16A #09']
        self.EC_aircraft_Group01 = ['EC-130H #1']
        self.EC_aircraft_Group02 = ['EC-130H #2']
        self.E2K_aircraft = ['E-2K #1']  # 1架

        AIR_TO_AIR_GROUPS = ['A2A_GROUP_1', 'A2A_GROUP_2']
        AIR_TO_SURFACE_GROUPS = ['A2S_GROUP_1', 'A2S_GROUP_2']
        ELECTRONIC_JAMMER_GROUPS = ['EJ_1', 'EJ_2']

        self.F16ATS_Group_agent_IDs = [f'F16_ATS_agent_{i}' for i in range(2)]
        self.F16ATA_Group_agent_IDs = [f'F16_ATA_agent_{i}' for i in range(2)]
        self.EC_Group_agent_IDs = [f'EC_agent_{i}' for i in range(2)]
        self.agent_IDs = self.F16ATA_Group_agent_IDs + self.F16ATS_Group_agent_IDs

        self.AIR_TO_AIR_GROUPS = ['A2A_GROUP_1', 'A2A_GROUP_2']
        self.AIR_TO_SURFACE_GROUPS = ['A2S_GROUP_1', 'A2S_GROUP_2']
        self.ELECTRONIC_JAMMER_GROUPS = ['EJ_1', 'EJ_2']
        self.operation_agent = self.AIR_TO_AIR_GROUPS + self.AIR_TO_SURFACE_GROUPS

        self.Agent_IDs_TO_Agent = dict(zip(self.agent_IDs, self.operation_agent))
        self.Agent_TO_Agent_IDs = dict(zip(self.operation_agent, self.agent_IDs))

        self.air_to_air_groups = {AIR_TO_AIR_GROUPS[i]: {} for i in range(len(AIR_TO_AIR_GROUPS))}
        self.air_to_air_group_name_to_guid = {AIR_TO_AIR_GROUPS[i]: None for i in range(len(AIR_TO_AIR_GROUPS))}
        self.air_to_surface_groups = {AIR_TO_SURFACE_GROUPS[i]: {} for i in range(len(AIR_TO_SURFACE_GROUPS))}
        self.air_to_surface_group_name_to_guid = \
            {AIR_TO_SURFACE_GROUPS[i]: None for i in range(len(AIR_TO_SURFACE_GROUPS))}
        self.electronic_jammer_groups = {ELECTRONIC_JAMMER_GROUPS[i]: {} for i in range(len(ELECTRONIC_JAMMER_GROUPS))}
        self.electronic_jammer_group_name_to_guid = \
            {ELECTRONIC_JAMMER_GROUPS[i]: None for i in range(len(ELECTRONIC_JAMMER_GROUPS))}

        self.operation_groups_name_to_guid = {}
        self.operation_groups = {}
        self.operation_groups_status = {op: False for op in self.operation_agent}
        self.operation_groups_class = {}
        self.no_nav_area_info = {}

        self.agent_reward = {agent_id: 0.0 for agent_id in self.agent_IDs}
        self.agent_action_mask = {agent_id: {"action_mask": None, } for agent_id in self.agent_IDs}

    def reset(self):

        self.steps = 0
        self._get_initial_state()

        self.time = self.scenario.m_Duration.split('@')
        self.m_StartTime = self.scenario.m_StartTime
        self.m_Time = self.scenario.m_Time
        self.side_name = self.env_config['side_name']
        self.enemy_side_name = self.env_config['enemy_side_name']
        self.side = self.scenario.get_side_by_name(self.side_name)
        self.enemy_side = self.scenario.get_side_by_name(self.enemy_side_name)
        self._init_data_structure()

        self.E2K_support_mission_startUp()
        self.EC_support_mission_startUp()
        self.env.step()
        self.set_up_EC_support_mission()

        self.create_operation_groups()

        self.operation_group_out()
        self.scenario = self.env.step()
        self._update(self.scenario)

        self.operation_group_name_to_guid()
        self.scenario = self.env.step()
        self._update(self.scenario)

        for k, v in self.operation_groups_name_to_guid.items():
            if k in (self.AIR_TO_AIR_GROUPS[0] + self.AIR_TO_SURFACE_GROUPS[0]):
                self.control_operation_group_next_point(k, self.EJ_G1_sp_RP_lat, self.EJ_G1_sp_RP_lon)
            if k in (self.AIR_TO_AIR_GROUPS[1] + self.AIR_TO_SURFACE_GROUPS[1]):
                self.control_operation_group_next_point(k, self.EJ_G2_sp_RP_lat, self.EJ_G2_sp_RP_lon)

        while self.env.scenario.m_Time - self.m_StartTime <= 1200:
            self.env.step()

        self.initial()

        self.Map_center_point = self.get_Map_center_point()
        self.max_lat = self.Map_center_point['latitude'] + 3
        self.min_lat = self.Map_center_point['latitude'] - 3
        self.max_lon = self.Map_center_point['longitude'] + 3
        self.min_lon = self.Map_center_point['longitude'] - 3
        self.set_combat_area_ref_point()

        self.scenario = self.env.step()
        self._update(self.scenario)

        self.agent_last_distance_to_target = {agent_id: 0 for agent_id in self.agent_IDs}
        self.agent_now_distance_to_target = {agent_id: 0 for agent_id in self.agent_IDs}
        self.update_distance_init()
        self.last_score = 0

        observation = self.get_observation_for_all_agents()
        return observation

    def get_battlefield_time(self):
        """"""
        t = 0
        try:
            tx = self.scenario.m_Time
            t = int(tx)
        except:
            traceback.print_exc()
        return time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime(t)), t


    def E2K_support_mission_startUp(self):
        self.E2K_air = [v for k, v in self.side.aircrafts.items() if v.strName in self.E2K_aircraft]
        if self.E2K_air:
            E2K_air = self.E2K_air[0]
        else:
            print("There is not E2K!!!")
        self.YuanDuan_sp_RP = {'latitude': (20, 11, 45), 'longitude': (122, 19, 3)}
        self.JinDuan_sp_RP = {'latitude': (21, 28, 46), 'longitude': (121, 12, 15)}

        self.YuanDuan_sp_RP_lat = LatLng_Rad2Dec(self.YuanDuan_sp_RP['latitude'][0],
                                                 self.YuanDuan_sp_RP['latitude'][1],
                                                 self.YuanDuan_sp_RP['latitude'][2]
                                                 )
        self.YuanDuan_sp_RP_lon = LatLng_Rad2Dec(self.YuanDuan_sp_RP['longitude'][0],
                                                 self.YuanDuan_sp_RP['longitude'][1],
                                                 self.YuanDuan_sp_RP['longitude'][2]
                                                 )
        self.JinDuan_sp_RP_lat = LatLng_Rad2Dec(self.JinDuan_sp_RP['latitude'][0],
                                                self.JinDuan_sp_RP['latitude'][1],
                                                self.JinDuan_sp_RP['latitude'][2]
                                                )
        self.JinDuan_sp_RP_lon = LatLng_Rad2Dec(self.JinDuan_sp_RP['longitude'][0],
                                                self.JinDuan_sp_RP['longitude'][1],
                                                self.JinDuan_sp_RP['longitude'][2]
                                                )


        self.side.add_reference_point('YJ_rp', self.JinDuan_sp_RP_lat, self.JinDuan_sp_RP_lon)

        self.E2K_sp_mission_name = 'E2K_support_mission'
        support_mission_E2K = self.side.add_mission_support(self.E2K_sp_mission_name, ['YJ_rp', ])
        support_mission_E2K.assign_unit(E2K_air.strGuid, 'false')
        support_mission_E2K.set_one_third_rule('false')
        support_mission_E2K.set_start_time('2021-05-26 10:55:43')
        support_mission_E2K.set_end_time('2021-05-26 14:15:43')
        support_mission_E2K.set_is_active('true')
        support_mission_E2K.switch_radar(True)
        support_mission_E2K.set_throttle('transitThrottleAircraft', 'Full')
        support_mission_E2K.set_throttle('stationThrottleAircraft', 'Full')
        support_mission_E2K.set_flight_size(1)
        support_mission_E2K.set_flight_size_check(self.side.strName, self.E2K_sp_mission_name, 'false')

    def set_up_EC_support_mission(self):
        for k, v in self.env.blueside.missions.items():
            if v.strName in [self.EJ_G1_sp_mission_name, self.EJ_G2_sp_mission_name]:
                doctrine = v.get_doctrine()
                if doctrine:
                    doctrine.set_em_control_status(em_item='Radar', status='Active')
                    doctrine.set_em_control_status(em_item='OECM', status='Active')

    def EC_support_mission_startUp(self):
        self.EC_G1 = [v for k, v in self.side.aircrafts.items() if v.strName in self.EC_aircraft_Group01]
        self.EC_G2 = [v for k, v in self.side.aircrafts.items() if v.strName in self.EC_aircraft_Group02]
        assert len(self.EC_G1) == 1
        assert len(self.EC_G1) == 1

        EJ_G1_air = self.EC_G1[0]
        EJ_G2_air = self.EC_G2[0]

        self.EJ_G1_sp_RP = {'latitude': (20, 43, 42), 'longitude': (121, 46, 17)}
        self.EJ_G2_sp_RP = {'latitude': (21, 33, 26), 'longitude': (123, 15, 30)}

        self.EJ_G1_sp_RP_lat = LatLng_Rad2Dec(self.EJ_G1_sp_RP['latitude'][0],
                                              self.EJ_G1_sp_RP['latitude'][1],
                                              self.EJ_G1_sp_RP['latitude'][2]
                                              )
        self.EJ_G1_sp_RP_lon = LatLng_Rad2Dec(self.EJ_G1_sp_RP['longitude'][0],
                                              self.EJ_G1_sp_RP['longitude'][1],
                                              self.EJ_G1_sp_RP['longitude'][2]
                                              )
        self.EJ_G2_sp_RP_lat = LatLng_Rad2Dec(self.EJ_G2_sp_RP['latitude'][0],
                                              self.EJ_G2_sp_RP['latitude'][1],
                                              self.EJ_G2_sp_RP['latitude'][2]
                                              )
        self.EJ_G2_sp_RP_lon = LatLng_Rad2Dec(self.EJ_G2_sp_RP['longitude'][0],
                                              self.EJ_G2_sp_RP['longitude'][1],
                                              self.EJ_G2_sp_RP['longitude'][2]
                                              )
        self.side.add_reference_point('EJ_G1_YJ_rp', self.EJ_G1_sp_RP_lat, self.EJ_G1_sp_RP_lon)
        self.side.add_reference_point('EJ_G2_YJ_rp', self.EJ_G2_sp_RP_lat, self.EJ_G2_sp_RP_lon)

        self.EJ_G1_sp_mission_name = 'EJ_G1_support_mission'
        self.EJ_G2_sp_mission_name = 'EJ_G2_support_mission'
        support_mission_EJ_G1 = self.side.add_mission_support(self.EJ_G1_sp_mission_name, ['EJ_G1_YJ_rp', ])
        support_mission_EJ_G2 = self.side.add_mission_support(self.EJ_G2_sp_mission_name, ['EJ_G2_YJ_rp', ])
        support_mission_EJ_G1.assign_unit(EJ_G1_air.strGuid, 'false')
        support_mission_EJ_G2.assign_unit(EJ_G2_air.strGuid, 'false')
        support_mission_EJ_G1.set_one_third_rule('false')
        support_mission_EJ_G2.set_one_third_rule('false')
        support_mission_EJ_G1.set_start_time('2021-05-26 10:55:43')
        support_mission_EJ_G2.set_start_time('2021-05-26 10:55:43')
        support_mission_EJ_G1.set_end_time('2021-05-26 14:15:43')
        support_mission_EJ_G2.set_end_time('2021-05-26 14:15:43')
        support_mission_EJ_G1.set_is_active('true')
        support_mission_EJ_G2.set_is_active('true')
        support_mission_EJ_G1.switch_radar(True)
        support_mission_EJ_G2.switch_radar(True)

        support_mission_EJ_G1.set_throttle('transitThrottleAircraft', 'Full')
        support_mission_EJ_G1.set_throttle('stationThrottleAircraft', 'Full')
        support_mission_EJ_G1.set_flight_size(1)
        support_mission_EJ_G1.set_flight_size_check(self.side.strName, self.E2K_sp_mission_name, 'false')
        support_mission_EJ_G2.set_throttle('transitThrottleAircraft', 'Full')
        support_mission_EJ_G2.set_throttle('stationThrottleAircraft', 'Full')
        support_mission_EJ_G2.set_flight_size(1)
        support_mission_EJ_G2.set_flight_size_check(self.side.strName, self.E2K_sp_mission_name, 'false')

    def get_Map_center_point(self):
        left_up_point = [22.9, 120.3]
        right_down_point = [16.4, 127.1]

        Map_center_point = MidPointCoordinate(left_up_point[0], left_up_point[1], right_down_point[0],
                                              right_down_point[1])

        return Map_center_point

    def update_distance_init(self):
        self.agent_last_distance_to_target = {agent_id: 0 for agent_id in self.agent_IDs}
        for k, v in self.operation_groups_class.items():
            self.agent_last_distance_to_target[self.Agent_TO_Agent_IDs[k]] = get_two_point_distance(v.dLongitude,
                                                                                                    v.dLatitude,
                                                                                                    self.ship_HM[
                                                                                                        'dLongitude'],
                                                                                                    self.ship_HM[
                                                                                                        'dLatitude'])

    def update_enemy_air_and_ship_GUID(self):
        self.enemy_air_GUID = {v.strGuid: [v.strName, None, False] for k, v in self.enemy_side.aircrafts.items()}
        self.enemy_ship_GUID = {v.strGuid: [v.strName, None, False] for k, v in self.enemy_side.ships.items()}
        for k, v in self.attack_ships.items():
            if v.m_ActualUnit in self.enemy_ship_GUID.keys():
                self.enemy_ship_GUID[v.m_ActualUnit][1] = v.strGuid
                self.enemy_ship_GUID[v.m_ActualUnit][2] = True
        for k, v in self.attack_airs.items():
            if v.m_ActualUnit in self.enemy_air_GUID.keys():
                self.enemy_air_GUID[v.m_ActualUnit][1] = v.strGuid
                self.enemy_air_GUID[v.m_ActualUnit][2] = True

    def update_air_status(self):
        self.aircrafts_controll_status = {agent_id: False for agent_id in self.agent_IDs}
        for k, v in self.side.aircrafts.items():
            if v.strAirOpsConditionString == 0:
                self.aircrafts_controll_status[self.Agent_TO_Agent_IDs[v.strName]] = True
            aircraft_doctrine = v.get_doctrine()
            if aircraft_doctrine == None:
                self.aircrafts_controll_status[self.Agent_TO_Agent_IDs[v.strName]] = False

    def initial(self):
        doctrine = self.side.get_doctrine()
        doctrine.set_fuel_state_for_aircraft('0')
        doctrine.set_weapon_control_status_land('0')
        doctrine.withdraw_on_damage('0')
        doctrine.set_weapon_state_for_aircraft('2002')
        doctrine.evade_automatically('true')
        doctrine.ignore_plotted_course('yes')
        doctrine.set_fuel_state_for_air_group('3')
        doctrine.set_weapon_state_for_air_group('3')
        doctrine.set_weapon_release_authority(weapon_dbid='718', target_type='2100',
                                              quantity_salvo=1, shooter_salvo='max',
                                              firing_range=35, self_defense='max', escort='')
        doctrine.set_weapon_release_authority(weapon_dbid='816', target_type='2999',
                                              quantity_salvo=12, shooter_salvo='max',
                                              firing_range='max', self_defense='max', escort='')
        doctrine.set_weapon_release_authority(weapon_dbid='816', target_type='3000',
                                              quantity_salvo=12, shooter_salvo='max',
                                              firing_range='max', self_defense='max', escort='')

        doctrine.set_weapon_release_authority('718', '2000', quantity_salvo=2, shooter_salvo='inherit',
                                              firing_range='max', self_defense='max',
                                              escort='')

    def step(self, action_dict):
        self.steps += 1
        self.last_operation_groups_status = self.operation_groups_status.copy()

        self.update_e2k_location()
        self.update_ec_location()

        if action_dict:
            self.assign_action_for_agents(action_dict)


        done = False
        if self.env_config['mode'] in ['train', 'development']:
            force_done = self.safe_step()
            if force_done:
                done = force_done
                self.reset_nums = 4
                print(f"{time.strftime('%H:%M:%S')} 在第{self.steps}步，强制重启墨子！！！")
            else:
                self._update(self.scenario)
                done = self._is_done()
        elif self.env_config['mode'] in ['versus', 'eval']:
            self.scenario = self.env.step()
            self._update(self.scenario)
            done = self._is_done()

        if done:
            print('-----------------------***-------------------------')
            print('Game Over!!!')
            print(self.side.m_ScoringLogs)
            print('++++Score:', self._get_win_score(), 'step:', self.steps)

        observation = self.get_observation_for_all_agents()
        reward = self.get_reward_for_all_agents()
        dones = self.set_dones_info_for_all_agents()
        infos = self.generate_agents_infos()
        # print(reward)
        # self.delete_no_units_mission()
        self.last_score = self._get_win_score()
        # self.set_weapon_doctrine()
        return observation, reward, dones, infos

    def check_done(self):
        done_flag = 0
        for agent_id, status in self.aircrafts_controll_status.items():
            if 'ATA' in agent_id or 'ATS' in agent_id:
                if status:
                    done_flag += 1
        if done_flag == 0:
            done = True
            # print('Over')
            return done
        else:
            return False

        if self.side.iTotalScore:
            return True  # 得分发生变化，也结束
        if self.mine_air:
            if self.enemy_air:
                done = False
        return done

    def _is_done(self):

        response_dic = self.scenario.get_responses()
        for _, v in response_dic.items():
            if v.Type == 'EndOfDeduction':
                # print('打印出标记：EndOfDeduction')
                return True
        All_status = []
        done_flag = 0
        for name, status in self.operation_groups_status.items():
            All_status.append(status)
        if all(All_status):
            done_flag += 1
        if done_flag == 1:
            return True

        return False

    def safe_step(self):
        force_done = False
        # noinspection PyBroadException
        try:
            pass
        except Exception:
            print(f"{time.strftime('%H:%M:%S')} 在第{self.steps}步，执行lua超时！！！")
            force_done = True
            return force_done
        # noinspection PyBroadException
        try:
            self.scenario.mozi_server.run_grpc_simulate()  # By Jcy 1018
            self.scenario = self.env.step()  # 墨子环境step
        except Exception:
            print(f"{time.strftime('%H:%M:%S')} 在第{self.steps}步，更新态势超时！！！")
            force_done = True
            return force_done
        if self.scenario and self.scenario.get_side_by_name(self.side_name):
            return force_done
        else:
            # 态势更新失败会抛出异常
            print(f"{time.strftime('%H:%M:%S')} 在第{self.steps}步，更新态势失败！！！")
            force_done = True
            return force_done

    def _update_operation_groups(self):
        temp_operation_groups = {}
        for group_name in self.operation_groups:
            temp_operation_groups[group_name] = {}
        for unit_guid, unit_class in self.agent_operation_units.items():
            for group_name, units in self.operation_groups.items():
                if unit_guid in units:
                    temp_operation_groups[group_name][unit_guid] = unit_class
        self.operation_groups = temp_operation_groups

    def _update(self, scenario):
        self.side = scenario.get_side_by_name(self.side_name)
        self.enemy_side = self.scenario.get_side_by_name(self.env_config['enemy_side_name'])

        RULE_OPERATION_UNITS_NAME = self.E2K_aircraft + self.EC_aircraft_Group01 + self.EC_aircraft_Group02

        self.agent_operation_units = {k: v for k, v in self.side.aircrafts.items()
                                      if v.strName not in RULE_OPERATION_UNITS_NAME}
        self.rule_operation_units = {k: v for k, v in self.side.aircrafts.items()
                                     if v.strName in RULE_OPERATION_UNITS_NAME}

        self.operation_groups_class = {v.strName: v
                                       for _, v in self.side.groups.items()}
        # self.operation_groups_class.update({v.strName: v
        #                                     for _, v in self.side.aircrafts.items()
        #                                     if 'EJ' in v.strName})

        self._update_operation_groups()
        self._update_operation_groups_status()

        self.attack_ships = {k: v
                             for k, v in self.side.contacts.items() if v.m_ContactType == 2}
        self.attack_airs = {k: v
                            for k, v in self.side.contacts.items() if v.m_ContactType == 0}
        if self.steps % 10 == 0:
            self.update_ships_no_nav_area()

        self.ship_HM = {}
        HM_flag = 0
        for k, v in self.side.contacts.items():
            if '航空母舰' in v.strName:
                self.ship_HM['dLongitude'] = v.dLongitude
                self.ship_HM['dLatitude'] = v.dLatitude
                HM_flag = 1
        if HM_flag == 0:
            self.ship_HM['dLongitude'] = float(124.9918595480383)
            self.ship_HM['dLatitude'] = float(18.917622952589632)
        # print('HM_flag:', HM_flag)

        # self.contacts_dict_prepare()

        self.m_Time = self.scenario.m_Time  # 想定当前时间

        # self.update_ewr_location()
        self.return_to_base()

    def update_e2k_location(self):
        # 规则控制的E-2K模块
        # 如果到达初始点就开始动态规划，没有到达就先前往
        unit_air = None
        change_time = self.env.scenario.m_Time - self.m_StartTime
        if change_time <= 1385:  # 执行原始的任务模块
            pass
        elif change_time > 1385:  # 调整支援点位置，决策是前进还是后退
            for unit_Guid, unit in self.side.aircrafts.items():
                if 'E-2K' in unit.strName:
                    unit_air = unit
            if unit_air:
                near_aircraft_num_200 = self.get_enemy_aircrafts_num_in_certain_distance(unit_air, 200 * 1000)
                if near_aircraft_num_200 == 0:
                    self.side.set_reference_point('YJ_rp', self.YuanDuan_sp_RP_lat, self.YuanDuan_sp_RP_lon)
                else:
                    self.side.set_reference_point('YJ_rp', self.JinDuan_sp_RP_lat, self.JinDuan_sp_RP_lon)

    def update_ec_location(self):
        # 规则控制的EC模块
        # 如果周围200公里没有敌人，则去找对应的反舰飞机，否则则往机场方向飞
        self.EC_G1 = [v for k, v in self.side.aircrafts.items() if v.strName in self.EC_aircraft_Group01]
        self.EC_G2 = [v for k, v in self.side.aircrafts.items() if v.strName in self.EC_aircraft_Group02]
        A2S_GROUP_1_class = None
        A2S_GROUP_2_class = None
        for k, v in self.operation_groups_class.items():
            if k == 'A2S_GROUP_1':
                A2S_GROUP_1_class = v
            elif k == 'A2S_GROUP_2':
                A2S_GROUP_2_class = v

        # assert len(self.EC_G1) == 1
        # assert len(self.EC_G1) == 1

        # EJ_G1_air = self.EC_G1[0]
        # EJ_G2_air = self.EC_G2[0]

        if A2S_GROUP_1_class:
            if self.EC_G1:
                EJ_G1_air = self.EC_G1[0]
                near_aircraft_num_200 = self.get_enemy_aircrafts_num_in_certain_distance(EJ_G1_air, 200 * 1000)
                if near_aircraft_num_200 == 0:
                    self.side.set_reference_point('EJ_G1_YJ_rp', A2S_GROUP_1_class.dLatitude,
                                                  A2S_GROUP_1_class.dLongitude)
                else:
                    self.side.set_reference_point('EJ_G1_YJ_rp', 22.674166666666668, 120.46194444444446)
        else:
            self.side.set_reference_point('EJ_G1_YJ_rp', 22.674166666666668, 120.46194444444446)

        if A2S_GROUP_2_class:
            if self.EC_G2:
                EJ_G2_air = self.EC_G2[0]
                near_aircraft_num_200 = self.get_enemy_aircrafts_num_in_certain_distance(EJ_G2_air, 200 * 1000)
                if near_aircraft_num_200 == 0:
                    self.side.set_reference_point('EJ_G2_YJ_rp', A2S_GROUP_2_class.dLatitude,
                                                  A2S_GROUP_2_class.dLongitude)
                else:
                    self.side.set_reference_point('EJ_G2_YJ_rp', 22.79420528061303, 121.18280624617334)
        else:
            self.side.set_reference_point('EJ_G2_YJ_rp', 22.79420528061303, 121.18280624617334)

    def get_observation_for_all_agents(self):
        """
        功能：获取所有智能体的观测并组合到一起
        :return:  {v.strName:v.obs}
        """
        multi_agent_observation = {}
        for operation_group_name, agent_id in self.Agent_TO_Agent_IDs.items():
            if self.operation_groups_status[operation_group_name]:
                if not self.last_operation_groups_status[operation_group_name]:
                    if "A2A" in operation_group_name:
                        multi_agent_observation[agent_id] = [0] * 37
                    elif "A2S" in operation_group_name:
                        multi_agent_observation[agent_id] = [0] * 39
                    else:
                        pass
            else:
                if "A2A" in operation_group_name:
                    multi_agent_observation[agent_id] = self._generate_operation_group_features(operation_group_name)
                elif "A2S" in operation_group_name:
                    multi_agent_observation[agent_id] = self._generate_operation_group_features_01(operation_group_name)
                else:
                    pass

        return multi_agent_observation

    def _generate_operation_group_features(self, operation_group_name):
        in_group_units = self.operation_groups[operation_group_name]

        feat_mine_air = [0] * 6
        group_class = None
        if operation_group_name in self.operation_groups_class.keys():
            group_class = self.operation_groups_class[operation_group_name]
            lat_relative = (group_class.dLatitude - self.Map_center_point['latitude']) * 2 / (
                    self.max_lat - self.min_lat)
            lon_relative = (group_class.dLongitude - self.Map_center_point['longitude']) * 2 / (
                    self.max_lon - self.min_lon)
            head = group_class.fCurrentHeading / 360.0
            speed = group_class.fCurrentSpeed * self.NauticalMiles_to_Kilometers / 1300.84
            fuel_state = 0
            air2air_missle_num = 0
            for unit_id, unit in in_group_units.items():
                fuel_state += unit.dFuelPercentage
                air2air_missle_num += self.get_weapon_num_on_aircraft(unit, '中程')
            fuel_state = fuel_state / len(in_group_units) / 100.0
            air2air_missle_num = air2air_missle_num / 12  # 中层空空导弹数量
            feat_mine_air = [lat_relative, lon_relative, head, speed, fuel_state, air2air_missle_num]

        # ————————————————————————————————————————————————————————————————————————————————————————————
        enemy_info = []  # 附近的2个敌方飞机信息
        enemy_info_air = [0] * 6
        if self.attack_airs and group_class:
            enemy_airs = self.attack_airs.copy()
            for i in range(2):
                con_air_mindistance, key = self.min_distance_to_enemy_air(group_class, enemy_airs)
                if con_air_mindistance:
                    unit_object = con_air_mindistance
                    enemy_info_air[i * 3 + 0] = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                                self.max_lat - self.min_lat)
                    enemy_info_air[i * 3 + 1] = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                                self.max_lon - self.min_lon)
                    enemy_info_air[i * 3 + 2] = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / 1300.84
                    # enemy_info_air[i * 4 + 3] = unit_object.fCurrentHeading / self.air_att_max['F16']['max_head']
                    enemy_airs.pop(key)
                else:
                    pass
        enemy_info = enemy_info_air
        # ————————————————————————————————————————————————————————————————————————————————————————————
        ally_info = []  # 附近两架战斗机信息、预警机信息、最近干扰机信息  2+ 2+ 16 + 6 = 26
        ally_info_E2K = [0] * 2
        ally_info_EC130H = [0] * 2

        E2K_longitude, E2K_latitude = self.get_lon_lat_by_airName(self.E2K_aircraft[0])
        if E2K_longitude:
            ally_info_E2K[0] = (E2K_latitude - self.Map_center_point['latitude']) * 2 / (self.max_lat - self.min_lat)
            ally_info_E2K[1] = (E2K_longitude - self.Map_center_point['longitude']) * 2 / (self.max_lon - self.min_lon)

        if operation_group_name == 'A2A_GROUP_1':
            EJ_longitude, EJ_latitude = self.get_lon_lat_by_airName(self.EC_aircraft_Group01[0])
            ally_info_EC130H[0] = (EJ_latitude - self.Map_center_point['latitude']) * 2 / (self.max_lat - self.min_lat)
            ally_info_EC130H[1] = (EJ_longitude - self.Map_center_point['longitude']) * 2 / (
                        self.max_lon - self.min_lon)
        if operation_group_name == 'A2A_GROUP_2':
            EJ_longitude, EJ_latitude = self.get_lon_lat_by_airName(self.EC_aircraft_Group02[0])
            ally_info_EC130H[0] = (EJ_latitude - self.Map_center_point['latitude']) * 2 / (self.max_lat - self.min_lat)
            ally_info_EC130H[1] = (EJ_longitude - self.Map_center_point['longitude']) * 2 / (
                        self.max_lon - self.min_lon)

        a2s_info = [0] * 7 * 2
        a2a_info = [0] * 6
        i = 0
        for k, v in self.operation_groups_class.items():
            if k != operation_group_name:
                if "A2S" in k:
                    a2s_info[i * 7 + 0] = (v.dLatitude - self.Map_center_point['latitude']) * 2 / (
                                self.max_lat - self.min_lat)
                    a2s_info[i * 7 + 1] = (v.dLongitude - self.Map_center_point['longitude']) * 2 / (
                                self.max_lon - self.min_lon)
                    a2s_info[i * 7 + 2] = v.fCurrentSpeed * self.NauticalMiles_to_Kilometers / 1300.84
                    a2s_info[i * 7 + 3] = 1  # 1表示反间编队，0表示空战编队
                    fuel_state = 0
                    air2air_missle_num = 0
                    air2ship_missle_num = 0
                    in_group_units = self.operation_groups[k]
                    for unit_id, unit in in_group_units.items():
                        fuel_state += unit.dFuelPercentage
                        air2air_missle_num += self.get_weapon_num_on_aircraft(unit, '中程')
                        air2ship_missle_num += self.get_weapon_num_on_aircraft(unit, '鱼叉')
                    a2s_info[i * 7 + 4] = air2air_missle_num / 12
                    a2s_info[i * 7 + 5] = air2ship_missle_num / 12
                    a2s_info[i * 7 + 6] = fuel_state / len(in_group_units) / 100.0
                    i = i + 1
                if "A2A" in k:
                    a2a_info[0] = (v.dLatitude - self.Map_center_point['latitude']) * 2 / (self.max_lat - self.min_lat)
                    a2a_info[1] = (v.dLongitude - self.Map_center_point['longitude']) * 2 / (
                                self.max_lon - self.min_lon)
                    a2a_info[2] = v.fCurrentSpeed * self.NauticalMiles_to_Kilometers / 1300.84
                    a2a_info[3] = 0  # 1表示反间编队，0表示空战编队
                    fuel_state = 0
                    air2air_missle_num = 0
                    in_group_units = self.operation_groups[k]
                    for unit_id, unit in in_group_units.items():
                        fuel_state += unit.dFuelPercentage
                        air2air_missle_num += self.get_weapon_num_on_aircraft(unit, '中程')
                    a2a_info[4] = air2air_missle_num / 12
                    a2a_info[5] = fuel_state / len(in_group_units) / 100.0

        ally_info = ally_info_E2K + ally_info_EC130H + a2s_info + a2a_info

        sim_degree = self.get_simu_time_degree()
        feats = feat_mine_air + enemy_info + ally_info + [sim_degree]

        return feats

    def _generate_operation_group_features_01(self, operation_group_name):
        in_group_units = self.operation_groups[operation_group_name]

        feat_mine_air = [0] * 7
        group_class = None
        if operation_group_name in self.operation_groups_class.keys():
            group_class = self.operation_groups_class[operation_group_name]
            lat_relative = (group_class.dLatitude - self.Map_center_point['latitude']) * 2 / (
                    self.max_lat - self.min_lat)
            lon_relative = (group_class.dLongitude - self.Map_center_point['longitude']) * 2 / (
                    self.max_lon - self.min_lon)
            head = group_class.fCurrentHeading / 360.0
            speed = group_class.fCurrentSpeed * self.NauticalMiles_to_Kilometers / 1300.84
            fuel_state = 0
            air2air_missle_num = 0
            air2ship_missle_num = 0
            for unit_id, unit in in_group_units.items():
                fuel_state += unit.dFuelPercentage
                air2air_missle_num += self.get_weapon_num_on_aircraft(unit, '中程')
                air2ship_missle_num += self.get_weapon_num_on_aircraft(unit, '鱼叉')
            air2air_missle_num = air2air_missle_num / 12
            air2ship_missle_num = air2ship_missle_num / 12
            fuel_state = fuel_state / len(in_group_units) / 100.0
            feat_mine_air = [lat_relative, lon_relative, head, speed, fuel_state, air2air_missle_num,
                             air2ship_missle_num]

        # ————————————————————————————————————————————————————————————————————————————————————————————
        enemy_info = []  # 附近的2个敌方飞机信息
        enemy_info_air = [0] * 6
        if self.attack_airs and group_class:
            enemy_airs = self.attack_airs.copy()
            for i in range(2):
                con_air_mindistance, key = self.min_distance_to_enemy_air(group_class, enemy_airs)
                if con_air_mindistance:
                    unit_object = con_air_mindistance
                    enemy_info_air[i * 3 + 0] = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                            self.max_lat - self.min_lat)
                    enemy_info_air[i * 3 + 1] = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                            self.max_lon - self.min_lon)
                    enemy_info_air[i * 3 + 2] = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / 1300.84
                    # enemy_info_air[i * 4 + 3] = unit_object.fCurrentHeading / self.air_att_max['F16']['max_head']
                    enemy_airs.pop(key)
                else:
                    pass
        enemy_info_ship = [0] * 2
        if self.attack_ships and group_class:
            enemy_airs = self.attack_ships.copy()
            for i in range(1):
                con_air_mindistance, key = self.min_distance_to_enemy_air(group_class, enemy_airs)
                if con_air_mindistance:
                    unit_object = con_air_mindistance
                    enemy_info_air[i * 3 + 0] = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                            self.max_lat - self.min_lat)
                    enemy_info_air[i * 3 + 1] = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                            self.max_lon - self.min_lon)
                    # enemy_info_air[i * 4 + 3] = unit_object.fCurrentHeading / self.air_att_max['F16']['max_head']
                    enemy_airs.pop(key)
                else:
                    pass

        enemy_info = enemy_info_air + enemy_info_ship
        # ————————————————————————————————————————————————————————————————————————————————————————————
        ally_info = []  # 附近两架战斗机信息、预警机信息、最近干扰机信息  2+ 2+ 16 + 6 = 26
        ally_info_E2K = [0] * 2
        ally_info_EC130H = [0] * 2

        E2K_longitude, E2K_latitude = self.get_lon_lat_by_airName(self.E2K_aircraft[0])
        if E2K_longitude:
            ally_info_E2K[0] = (E2K_latitude - self.Map_center_point['latitude']) * 2 / (self.max_lat - self.min_lat)
            ally_info_E2K[1] = (E2K_longitude - self.Map_center_point['longitude']) * 2 / (self.max_lon - self.min_lon)

        if operation_group_name == 'A2S_GROUP_1':
            EJ_longitude, EJ_latitude = self.get_lon_lat_by_airName(self.EC_aircraft_Group01[0])
            ally_info_EC130H[0] = (EJ_latitude - self.Map_center_point['latitude']) * 2 / (self.max_lat - self.min_lat)
            ally_info_EC130H[1] = (EJ_longitude - self.Map_center_point['longitude']) * 2 / (
                    self.max_lon - self.min_lon)
        if operation_group_name == 'A2S_GROUP_2':
            EJ_longitude, EJ_latitude = self.get_lon_lat_by_airName(self.EC_aircraft_Group02[0])
            ally_info_EC130H[0] = (EJ_latitude - self.Map_center_point['latitude']) * 2 / (self.max_lat - self.min_lat)
            ally_info_EC130H[1] = (EJ_longitude - self.Map_center_point['longitude']) * 2 / (
                    self.max_lon - self.min_lon)

        a2s_info = [0] * 7
        a2a_info = [0] * 6 * 2
        i = 0
        for k, v in self.operation_groups_class.items():
            if k != operation_group_name:
                if "A2S" in k:
                    a2s_info[0] = (v.dLatitude - self.Map_center_point['latitude']) * 2 / (
                            self.max_lat - self.min_lat)
                    a2s_info[1] = (v.dLongitude - self.Map_center_point['longitude']) * 2 / (
                            self.max_lon - self.min_lon)
                    a2s_info[2] = v.fCurrentSpeed * self.NauticalMiles_to_Kilometers / 1300.84
                    a2s_info[3] = 1  # 1表示反间编队，0表示空战编队
                    fuel_state = 0
                    air2air_missle_num = 0
                    air2ship_missle_num = 0
                    in_group_units = self.operation_groups[k]
                    for unit_id, unit in in_group_units.items():
                        fuel_state += unit.dFuelPercentage
                        air2air_missle_num += self.get_weapon_num_on_aircraft(unit, '中程')
                        air2ship_missle_num += self.get_weapon_num_on_aircraft(unit, '鱼叉')
                    a2s_info[4] = fuel_state / len(in_group_units) / 100.0
                    a2s_info[5] = air2air_missle_num / 12
                    a2s_info[6] = air2ship_missle_num / 12
                if "A2A" in k:
                    a2a_info[i * 6 + 0] = (v.dLatitude - self.Map_center_point['latitude']) * 2 / (
                                self.max_lat - self.min_lat)
                    a2a_info[i * 6 + 1] = (v.dLongitude - self.Map_center_point['longitude']) * 2 / (
                            self.max_lon - self.min_lon)
                    a2a_info[i * 6 + 2] = v.fCurrentSpeed * self.NauticalMiles_to_Kilometers / 1300.84
                    a2a_info[i * 6 + 3] = 0  # 1表示反间编队，0表示空战编队
                    fuel_state = 0
                    air2air_missle_num = 0
                    in_group_units = self.operation_groups[k]
                    for unit_id, unit in in_group_units.items():
                        fuel_state += unit.dFuelPercentage
                        air2air_missle_num += self.get_weapon_num_on_aircraft(unit, '中程')
                    a2a_info[i * 6 + 4] = air2air_missle_num / 12
                    a2a_info[i * 6 + 5] = fuel_state / len(in_group_units) / 100.0
                    i = i + 1

        ally_info = ally_info_E2K + ally_info_EC130H + a2s_info + a2a_info

        sim_degree = self.get_simu_time_degree()
        feats = feat_mine_air + enemy_info + ally_info + [sim_degree]

        return feats

    @staticmethod
    def _get_weapon_num(weapon_list, weapon_type):
        num = 0
        for weapon in weapon_list:
            if weapon[0] != '' and weapon[-1] != '':
                if int(re.sub('\D', '', weapon[-1])) in weapon_type:
                    num += int(weapon[0])
        return num

    def get_F16ATA_Action_Mask(self, air_strName):
        action_mask = [1] * 30
        throttle_choice = [1, 2, 3, 4]
        speed_choice = [648.2, 888.96, 1055.64, 1259.36]
        head_choice = [i * 60 for i in range(6)]
        unit_air = None
        for unit_Guid, unit in self.side.aircrafts.items():
            if unit.strName == air_strName:
                unit_air = unit
        for throttle in throttle_choice:
            for head in head_choice:
                speed = speed_choice[throttle - 1]
                distance_D = speed / 3600 * (etc.DURATION_INTERVAL)
                new_point = get_point_with_point_bearing_distance(unit_air.dLatitude, unit_air.dLongitude,
                                                                  head, distance=distance_D)
                if new_point["latitude"] < self.min_lat or new_point["latitude"] > self.max_lat:
                    index = (throttle - 1) * 6 + int(head / 60)
                    action_mask[index] = 0
                if new_point["longitude"] < self.min_lon or new_point["longitude"] > self.max_lon:
                    index = (throttle - 1) * 6 + int(head / 60)
                    action_mask[index] = 0
        head_choice = [i * 60 for i in range(6)]
        air_head = unit_air.fCurrentHeading
        air_head_next_max = air_head + 90
        air_head_next_min = air_head - 90
        if air_head_next_max >= 360:
            qujian1 = [0, air_head_next_max - 360]
            qujian2 = [air_head_next_min, 360]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1] or qujian2[0] <= head <= qujian2[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
                    action_mask[i + 18] = 0
        elif air_head_next_min <= 0:
            qujian1 = [0, air_head_next_max]
            qujian2 = [air_head_next_min + 360, 360]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1] or qujian2[0] <= head <= qujian2[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
                    action_mask[i + 18] = 0
        else:
            qujian1 = [air_head_next_min, air_head_next_max]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
                    action_mask[i + 18] = 0
        return action_mask

    def get_F16ATS_Action_Mask(self, air_strName):
        action_mask = [1] * 34
        throttle_choice = [1, 2, 3, 4]
        speed_choice = [648.2, 888.96, 1055.64, 1259.36]
        head_choice = [i * 60 for i in range(6)]
        unit_air = None
        for unit_Guid, unit in self.side.aircrafts.items():
            if unit.strName == air_strName:
                unit_air = unit
        for throttle in throttle_choice:
            for head in head_choice:
                speed = speed_choice[throttle - 1]
                distance_D = speed / 3600 * (etc.DURATION_INTERVAL)
                new_point = get_point_with_point_bearing_distance(unit_air.dLatitude, unit_air.dLongitude,
                                                                  head, distance=distance_D)
                if new_point["latitude"] < self.min_lat or new_point["latitude"] > self.max_lat:
                    # ban_throttle_head.append([throttle, head])
                    index = (throttle - 1) * 6 + int(head / 60)
                    action_mask[index] = 0
                if new_point["longitude"] < self.min_lon or new_point["longitude"] > self.max_lon:
                    index = (throttle - 1) * 6 + int(head / 60)
                    action_mask[index] = 0
        head_choice = [i * 60 for i in range(6)]
        air_head = unit_air.fCurrentHeading
        air_head_next_max = air_head + 90
        air_head_next_min = air_head - 90
        if air_head_next_max >= 360:
            qujian1 = [0, air_head_next_max - 360]
            qujian2 = [air_head_next_min, 360]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1] or qujian2[0] <= head <= qujian2[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
                    action_mask[i + 18] = 0
        elif air_head_next_min <= 0:
            qujian1 = [0, air_head_next_max]
            qujian2 = [air_head_next_min + 360, 360]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1] or qujian2[0] <= head <= qujian2[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
                    action_mask[i + 18] = 0
        else:
            qujian1 = [air_head_next_min, air_head_next_max]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
                    action_mask[i + 18] = 0
        return action_mask

    def get_E2K_Action_Mask(self, air_strName):
        action_mask = [1] * 18
        throttle_choice = [1, 2, 3]
        speed_choice = [370.4, 592.64, 620.42]
        head_choice = [i * 60 for i in range(6)]
        unit_air = None
        for unit_Guid, unit in self.side.aircrafts.items():
            if unit.strName == air_strName:
                unit_air = unit
        for throttle in throttle_choice:
            for head in head_choice:
                speed = speed_choice[throttle - 1]
                distance_D = speed / 3600 * (etc.DURATION_INTERVAL)
                new_point = get_point_with_point_bearing_distance(unit_air.dLatitude, unit_air.dLongitude,
                                                                  head, distance=distance_D)
                if new_point["latitude"] < self.min_lat or new_point["latitude"] > self.max_lat:
                    # ban_throttle_head.append([throttle, head])
                    index = (throttle - 1) * 6 + int(head / 60)
                    action_mask[index] = 0
                if new_point["longitude"] < self.min_lon or new_point["longitude"] > self.max_lon:
                    index = (throttle - 1) * 6 + int(head / 60)
                    action_mask[index] = 0
        head_choice = [i * 60 for i in range(6)]
        air_head = unit_air.fCurrentHeading
        air_head_next_max = air_head + 90
        air_head_next_min = air_head - 90
        if air_head_next_max >= 360:
            qujian1 = [0, air_head_next_max - 360]
            qujian2 = [air_head_next_min, 360]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1] or qujian2[0] <= head <= qujian2[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
        elif air_head_next_min <= 0:
            qujian1 = [0, air_head_next_max]
            qujian2 = [air_head_next_min + 360, 360]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1] or qujian2[0] <= head <= qujian2[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
        else:
            qujian1 = [air_head_next_min, air_head_next_max]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
        return action_mask

    def get_EC130H_Action_Mask(self, air_strName):
        action_mask = [1] * 18
        throttle_choice = [1, 2, 3]
        speed_choice = [333.36, 370.4, 425.96]
        head_choice = [i * 60 for i in range(6)]
        unit_air = None
        for unit_Guid, unit in self.side.aircrafts.items():
            if unit.strName == air_strName:
                unit_air = unit
        for throttle in throttle_choice:
            for head in head_choice:
                speed = speed_choice[throttle - 1]
                distance_D = speed / 3600 * (etc.DURATION_INTERVAL)
                new_point = get_point_with_point_bearing_distance(unit_air.dLatitude, unit_air.dLongitude,
                                                                  head, distance=distance_D)
                if new_point["latitude"] < self.min_lat or new_point["latitude"] > self.max_lat:
                    # ban_throttle_head.append([throttle, head])
                    index = (throttle - 1) * 6 + int(head / 60)
                    action_mask[index] = 0
                if new_point["longitude"] < self.min_lon or new_point["longitude"] > self.max_lon:
                    index = (throttle - 1) * 6 + int(head / 60)
                    action_mask[index] = 0
        head_choice = [i * 60 for i in range(6)]
        air_head = unit_air.fCurrentHeading
        air_head_next_max = air_head + 90
        air_head_next_min = air_head - 90
        if air_head_next_max >= 360:
            qujian1 = [0, air_head_next_max - 360]
            qujian2 = [air_head_next_min, 360]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1] or qujian2[0] <= head <= qujian2[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
        elif air_head_next_min <= 0:
            qujian1 = [0, air_head_next_max]
            qujian2 = [air_head_next_min + 360, 360]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1] or qujian2[0] <= head <= qujian2[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
        else:
            qujian1 = [air_head_next_min, air_head_next_max]
            for i in range(len(head_choice)):
                head = head_choice[i]
                if qujian1[0] <= head <= qujian1[1]:
                    pass
                else:
                    ban_head = head
                    action_mask[i] = 0
                    action_mask[i + 6] = 0
                    action_mask[i + 12] = 0
        return action_mask

    def get_E2K_observation(self, air_strName):
        my_info = self.get_aircraft_self_info_E2K(air_strName)
        enemy_info = self.get_contact_enmeys_info_E2K(air_strName)
        extra_info = self.get_extra_info()  # 特征信息可以扩展的接口
        E2K_observation = my_info + enemy_info + extra_info
        return E2K_observation

    def get_extra_info(self):
        sim_degree = self.get_simu_time_degree()
        return [sim_degree, ]

    def get_simu_time_degree(self):
        total_time = 200 * 60
        current_time = self.scenario.m_Time  # 当前时间
        time_cost = current_time - self.m_StartTime
        simu_degree = (time_cost / total_time) * 100 / 100
        return simu_degree

    def get_aircraft_self_info_E2K(self, air_strName):
        feat_mine_air = [0] * 9
        unit_object = None
        for unit_Guid, unit in self.side.aircrafts.items():
            if unit.strName == air_strName:
                unit_object = unit
        if unit_object:
            lat_relative = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                        self.max_lat - self.min_lat)
            lon_relative = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                        self.max_lon - self.min_lon)
            head = unit_object.fCurrentHeading / self.air_att_max['E-2K']['max_head']
            alt = unit_object.fCurrentAlt / self.air_att_max['E-2K']['max_altitude']
            speed = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / self.air_att_max['E-2K']['max_speed']
            des_alt = unit_object.fDesiredAlt / self.air_att_max['E-2K']['max_altitude']
            des_speed = unit_object.fDesiredSpeed * self.NauticalMiles_to_Kilometers / self.air_att_max['E-2K'][
                'max_speed']
            fuel_state = unit_object.dFuelPercentage / 100.0  # 燃油百分比
            extra_info_near_ally_aircraft_num = self.get_our_aircrafts_num_in_certain_distance(unit_object,
                                                                                               search_distance=140 * 1000) / 18.0
            feat_mine_air = [lat_relative, lon_relative, head, alt, speed, des_alt, des_speed, fuel_state,
                             extra_info_near_ally_aircraft_num]
        return feat_mine_air

    def get_contact_enmeys_info_E2K(self, air_strName):
        enemy_info = [0] * 7
        con_air_mindistance, key = self.min_distance_to_enemy_air(air_strName, self.attack_airs)
        if con_air_mindistance:
            unit_object = con_air_mindistance
            lat_relative = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                    self.max_lat - self.min_lat)
            lon_relative = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                    self.max_lon - self.min_lon)
            head = unit_object.fCurrentHeading / self.air_att_max['F16']['max_head']
            speed = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / self.air_att_max['F16']['max_speed']
        else:
            lat_relative = 0
            lon_relative = 0
            head = 0
            speed = 0
        unit_object = None
        for unit_Guid, unit in self.side.aircrafts.items():
            if unit.strName == air_strName:
                unit_object = unit
        if unit_object:
            near_aircraft_num_140 = self.get_enemy_aircrafts_num_in_certain_distance(unit_object, 140 * 1000)
        else:
            near_aircraft_num_140 = 0
        if unit_object:
            near_aircraft_num_400 = self.get_enemy_aircrafts_num_in_certain_distance(unit_object, 400 * 1000)
        else:
            near_aircraft_num_400 = 0
        if unit_object:
            near_ship_num_400 = self.get_enemy_ship_num_in_certain_distance(unit_object, 400 * 1000)
        else:
            near_ship_num_400 = 0

        enemy_info = [lat_relative, lon_relative, head, speed, near_aircraft_num_140, near_aircraft_num_400,
                      near_ship_num_400]
        return enemy_info

    def get_enemy_aircrafts_num_in_certain_distance(self, aircraft_object, search_distance):
        aircraft_num = 0
        lat_1 = aircraft_object.dLatitude
        long_1 = aircraft_object.dLongitude
        for k, other_aircraft in self.attack_airs.items():
            lat_2 = other_aircraft.dLatitude
            long_2 = other_aircraft.dLongitude
            pair_distance = get_two_point_distance(long_1, lat_1, long_2, lat_2)
            if pair_distance <= search_distance:
                aircraft_num = aircraft_num + 1

        return aircraft_num

    def get_enemy_ship_num_in_certain_distance(self, aircraft_object, search_distance):
        aircraft_num = 0
        lat_1 = aircraft_object.dLatitude
        long_1 = aircraft_object.dLongitude
        for k, other_ship in self.attack_ships.items():
            lat_2 = other_ship.dLatitude
            long_2 = other_ship.dLongitude
            pair_distance = get_two_point_distance(long_1, lat_1, long_2, lat_2)
            if pair_distance <= search_distance:
                aircraft_num = aircraft_num + 1
        return aircraft_num

    def get_our_aircrafts_num_in_certain_distance(self, aircraft_object, search_distance):
        aircraft_num = 0
        lat_1 = aircraft_object.dLatitude
        long_1 = aircraft_object.dLongitude
        for k, other_aircraft in self.side.aircrafts.items():
            if other_aircraft.strName != aircraft_object.strName and 'E2-K' not in other_aircraft.strName and 'EC' not in other_aircraft.strName:
                lat_2 = other_aircraft.dLatitude
                long_2 = other_aircraft.dLongitude
                pair_distance = get_two_point_distance(long_1, lat_1, long_2, lat_2)
                if pair_distance <= search_distance:
                    aircraft_num = aircraft_num + 1
        return aircraft_num

    def get_EC130H_observation(self, air_strName):
        my_info = self.get_aircraft_self_info_EC130H(air_strName)
        enemy_info = self.get_contact_enmeys_info_EC130H(air_strName)
        extra_info = self.get_extra_info()  # 特征信息可以扩展的接口
        EC130H_observation = my_info + enemy_info + extra_info
        return EC130H_observation

    def get_aircraft_self_info_EC130H(self, air_strName):
        feat_mine_air = [0] * 9
        unit_object = None
        for unit_Guid, unit in self.side.aircrafts.items():
            if unit.strName == air_strName:
                unit_object = unit
        if unit_object:
            lat_relative = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                        self.max_lat - self.min_lat)
            lon_relative = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                        self.max_lon - self.min_lon)
            head = unit_object.fCurrentHeading / self.air_att_max['EC130H']['max_head']
            alt = unit_object.fCurrentAlt / self.air_att_max['EC130H']['max_altitude']
            speed = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / self.air_att_max['EC130H'][
                'max_speed']
            des_alt = unit_object.fDesiredAlt / self.air_att_max['EC130H']['max_altitude']
            des_speed = unit_object.fDesiredSpeed * self.NauticalMiles_to_Kilometers / self.air_att_max['EC130H'][
                'max_speed']
            fuel_state = unit_object.dFuelPercentage / 100.0
            extra_info_near_ally_aircraft_num = self.get_our_aircrafts_num_in_certain_distance(unit_object,
                                                                                               search_distance=140 * 1000) / 18.0
            feat_mine_air = [lat_relative, lon_relative, head, alt, speed, des_alt, des_speed, fuel_state,
                             extra_info_near_ally_aircraft_num]
        return feat_mine_air

    def get_contact_enmeys_info_EC130H(self, air_strName):
        enemy_info = [0] * 5
        con_air_mindistance, key = self.min_distance_to_enemy_air(air_strName, self.attack_airs)
        if con_air_mindistance:
            unit_object = con_air_mindistance
            lat_relative = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                    self.max_lat - self.min_lat)
            lon_relative = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                    self.max_lon - self.min_lon)
            head = unit_object.fCurrentHeading / self.air_att_max['F16']['max_head']
            speed = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / self.air_att_max['F16']['max_speed']
        else:
            lat_relative = 0
            lon_relative = 0
            head = 0
            speed = 0
        unit_object = None
        for unit_Guid, unit in self.side.aircrafts.items():
            if unit.strName == air_strName:
                unit_object = unit
        if unit_object:
            near_aircraft_num_140 = self.get_enemy_aircrafts_num_in_certain_distance(unit_object, 140 * 1000)
        else:
            near_aircraft_num_140 = 0

        enemy_info = [lat_relative, lon_relative, head, speed, near_aircraft_num_140]
        return enemy_info

    def min_distance_to_enemy_air(self, group_class, enemy_airs):
        air_longitude = group_class.dLongitude
        air_latitude = group_class.dLatitude

        distance_min = 1000000
        con_air_mindistance = None
        key = None
        for k, v in enemy_airs.items():
            # if '米格' in v.strName:
            con_lat = v.dLatitude
            con_long = v.dLongitude
            distance = get_two_point_distance(air_longitude, air_latitude, con_long, con_lat)
            if distance < distance_min:
                distance_min = distance
                con_air_mindistance = v
                key = k
        return con_air_mindistance, key

    def min_distance_to_enemy_missle(self, air_strName):
        air_list = [v for k, v in self.side.aircrafts.items() if v.strName == air_strName]
        air_longitude = air_list[0].dLongitude
        air_latitude = air_list[0].dLatitude

        distance_min = 1000000
        con_missle_mindistance = None
        key = None
        for k, v in self.attack_missle.items():
            con_lat = v.dLatitude
            con_long = v.dLongitude
            distance = get_two_point_distance(air_longitude, air_latitude, con_long, con_lat)
            if distance < distance_min:
                distance_min = distance
                con_missle_mindistance = v
                key = k

        return con_missle_mindistance, k

    def aircraft_dict_prepare(self):
        self.E2K_aircraft_k_to_v = {k: v for k, v in self.side.aircrafts.items() if v.strName in self.E2K_aircraft}
        self.E2K_aircraft_kName_to_v = {v.strName: v for k, v in self.side.aircrafts.items() if
                                        v.strName in self.E2K_aircraft}

    def contacts_dict_prepare(self):
        self.attack_ships = {k: v for k, v in self.side.contacts.items() if v.m_ContactType == 2}  # 舰船
        self.attack_airs = {k: v for k, v in self.side.contacts.items() if v.m_ContactType == 0}  # 飞机
        self.attack_missle = {k: v for k, v in self.side.contacts.items() if v.m_ContactType == 1}  # 导弹

        self.attack_airs_MiGe = {k: v for k, v in self.attack_airs.items() if '米格' in v.strName}
        self.attack_airs_Ka = {k: v for k, v in self.attack_airs.items() if '卡' in v.strName}
        self.attack_ship_HM = {k: v for k, v in self.attack_ships.items() if '航空母舰' in v.strName}
        self.attack_ship_QZ = {k: v for k, v in self.attack_ships.items() if '驱逐舰' in v.strName}
        self.attack_ship_HW = {k: v for k, v in self.attack_ships.items() if '护卫舰' in v.strName}

    def get_lon_lat_by_airName(self, air_strName):
        air_list = [v for k, v in self.side.aircrafts.items() if v.strName == air_strName]
        if air_list:
            air_longitude = air_list[0].dLongitude
            air_latitude = air_list[0].dLatitude
            return air_longitude, air_latitude
        else:
            return 0, 0

    def get_F16ATS_observation(self, air_strName):
        feat_mine_air = [0] * 11
        unit_object = None
        for unit_Guid, unit in self.side.aircrafts.items():
            if unit.strName == air_strName:
                unit_object = unit
        if unit_object:
            lat_relative = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                    self.max_lat - self.min_lat)
            lon_relative = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                    self.max_lon - self.min_lon)
            head = unit_object.fCurrentHeading / self.air_att_max['F16']['max_head']
            alt = unit_object.fCurrentAlt / self.air_att_max['F16']['max_altitude']
            speed = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / self.air_att_max['F16']['max_speed']
            des_alt = unit_object.fDesiredAlt / self.air_att_max['F16']['max_altitude']
            des_speed = unit_object.fDesiredSpeed * self.NauticalMiles_to_Kilometers / self.air_att_max['F16'][
                'max_speed']
            fuel_state = unit_object.dFuelPercentage / 100.0
            extra_info_near_ally_aircraft_num = self.get_our_aircrafts_num_in_certain_distance(unit_object,
                                                                                               search_distance=100 * 1000) / 18.0
            air2air_missle_num = self.get_weapon_num_on_aircraft(unit_object, '中程') / 4
            XWS_missle_num = self.get_weapon_num_on_aircraft(unit_object, '响尾蛇') / 2

            feat_mine_air = [lat_relative, lon_relative, head, alt, speed, des_alt, des_speed, fuel_state,
                             extra_info_near_ally_aircraft_num, air2air_missle_num, XWS_missle_num]
        enemy_info = []
        enemy_info_air = [0] * 16
        enemy_info_ship = [0] * 6
        if self.attack_ship_HM:
            ship_list = [v for k, v in self.attack_ship_HM.items()]
            enemy_info_ship[0] = (ship_list[0].dLatitude - self.Map_center_point['latitude']) * 2 / (
                    self.max_lat - self.min_lat)
            enemy_info_ship[1] = (ship_list[0].dLongitude - self.Map_center_point['longitude']) * 2 / (
                    self.max_lon - self.min_lon)
        else:
            pass
        if self.attack_ship_QZ:
            ship_list = [v for k, v in self.attack_ship_QZ.items()]
            enemy_info_ship[2] = (ship_list[0].dLatitude - self.Map_center_point['latitude']) * 2 / (
                    self.max_lat - self.min_lat)
            enemy_info_ship[3] = (ship_list[0].dLongitude - self.Map_center_point['longitude']) * 2 / (
                    self.max_lon - self.min_lon)
        else:
            pass
        if self.attack_ship_HW:
            ship_list = [v for k, v in self.attack_ship_HW.items()]
            enemy_info_ship[4] = (ship_list[0].dLatitude - self.Map_center_point['latitude']) * 2 / (
                    self.max_lat - self.min_lat)
            enemy_info_ship[5] = (ship_list[0].dLongitude - self.Map_center_point['longitude']) * 2 / (
                    self.max_lon - self.min_lon)
        else:
            pass
        if self.attack_airs_MiGe:
            enemy_airs = self.attack_airs_MiGe.copy()
            for i in range(4):
                con_air_mindistance, key = self.min_distance_to_enemy_air(air_strName, enemy_airs)
                if con_air_mindistance:
                    unit_object = con_air_mindistance
                    enemy_info_air[i * 4 + 0] = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                            self.max_lat - self.min_lat)
                    enemy_info_air[i * 4 + 1] = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                            self.max_lon - self.min_lon)
                    enemy_info_air[i * 4 + 2] = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / \
                                                self.air_att_max['F16']['max_speed']
                    enemy_info_air[i * 4 + 3] = unit_object.fCurrentHeading / self.air_att_max['F16']['max_head']
                    enemy_airs.pop(key)
                else:
                    pass
        enemy_info = enemy_info_ship + enemy_info_air
        ally_info = []
        ally_info_fighter = [0] * 14
        ally_info_E2K = [0] * 2
        ally_info_EC130H = [0] * 2
        fight_air = self.air_air_aircraft + self.anti_ships_aircraft
        ally_airs = {k: v for k, v in self.side.aircrafts.items() if
                     v.strName != air_strName and v.strName in fight_air}
        if ally_airs:
            for i in range(2):
                ally_air_mindistance, key = self.min_distance_to_enemy_air(air_strName, ally_airs)
                if ally_air_mindistance:
                    unit_object = ally_air_mindistance
                    ally_info_fighter[i * 7 + 0] = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                            self.max_lat - self.min_lat)
                    ally_info_fighter[i * 7 + 1] = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                            self.max_lon - self.min_lon)
                    ally_info_fighter[i * 7 + 2] = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / \
                                                   self.air_att_max['F16']['max_speed']
                    ally_info_fighter[i * 7 + 3] = unit_object.fCurrentHeading / self.air_att_max['F16']['max_head']
                    ally_airs.pop(key)
                    if ally_air_mindistance.strName in self.air_air_aircraft:
                        ally_info_fighter[i * 7 + 4] = 1  # 表示飞机类型，空战飞机为1，反舰飞机为0
                        ally_info_fighter[i * 7 + 5] = self.get_weapon_num_on_aircraft(unit_object,
                                                                                       '中程') / 4  # 弹药量【AGM AIM】
                        ally_info_fighter[i * 7 + 6] = self.get_weapon_num_on_aircraft(unit_object, '响尾蛇') / 2
                    elif ally_air_mindistance.strName in self.anti_ships_aircraft:
                        ally_info_fighter[i * 7 + 4] = 0  # 表示飞机类型，空战飞机为1，反舰飞机为0
                        ally_info_fighter[i * 7 + 5] = self.get_weapon_num_on_aircraft(unit_object,
                                                                                       '中程') / 2  # 弹药量【AGM AIM】
                        ally_info_fighter[i * 7 + 6] = self.get_weapon_num_on_aircraft(unit_object, '鱼叉') / 2
                    else:
                        pass

        E2K_longitude, E2K_latitude = self.get_lon_lat_by_airName(self.E2K_aircraft[0])
        if E2K_longitude:
            ally_info_E2K[0] = (E2K_latitude - self.Map_center_point['latitude']) * 2 / (self.max_lat - self.min_lat)
            ally_info_E2K[1] = (E2K_longitude - self.Map_center_point['longitude']) * 2 / (self.max_lon - self.min_lon)

        EC130H_air_mindistance, key = self.min_distance_to_enemy_air(air_strName, self.EC130H_aircrafts_k_to_v)
        if EC130H_air_mindistance:
            ally_info_EC130H[0] = (EC130H_air_mindistance.dLatitude - self.Map_center_point['latitude']) * 2 / (
                    self.max_lat - self.min_lat)
            ally_info_EC130H[1] = (EC130H_air_mindistance.dLongitude - self.Map_center_point['longitude']) * 2 / (
                    self.max_lon - self.min_lon)

        ally_info = ally_info_fighter + ally_info_E2K + ally_info_EC130H

        extra_info = self.get_extra_info()  # 特征信息可以扩展的接口
        F16ATS_observation = feat_mine_air + enemy_info + ally_info + extra_info
        return F16ATS_observation

    def get_weapon_num_on_aircraft(self, aircraft_object, weapon_name):
        weapon_num = 0
        index_name = ''
        if weapon_name == '中程':
            index_name = 'AIM-120C'
        elif weapon_name == '响尾蛇':
            index_name = 'AIM-9M'
        elif weapon_name == '鱼叉':
            index_name = 'AGM'
        try:
            index_num = aircraft_object.m_UnitWeapons.index(index_name) - 3
            weapon_num = int(aircraft_object.m_UnitWeapons[index_num])
        except ValueError:  # 这个武器耗光
            return weapon_num
        return weapon_num

    def get_F16ATA_observation(self, air_strName):
        feat_mine_air = [0] * 11
        unit_object = None
        for unit_Guid, unit in self.side.aircrafts.items():
            if unit.strName == air_strName:
                unit_object = unit
        if unit_object:
            lat_relative = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                        self.max_lat - self.min_lat)
            lon_relative = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                        self.max_lon - self.min_lon)
            head = unit_object.fCurrentHeading / self.air_att_max['F16']['max_head']
            alt = unit_object.fCurrentAlt / self.air_att_max['F16']['max_altitude']
            speed = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / self.air_att_max['F16']['max_speed']
            des_alt = unit_object.fDesiredAlt / self.air_att_max['F16']['max_altitude']
            des_speed = unit_object.fDesiredSpeed * self.NauticalMiles_to_Kilometers / self.air_att_max['F16'][
                'max_speed']
            fuel_state = unit_object.dFuelPercentage / 100.0  # 燃油百分比
            extra_info_near_ally_aircraft_num = self.get_our_aircrafts_num_in_certain_distance(unit_object,
                                                                                               search_distance=100 * 1000) / 18.0
            air2air_missle_num = self.get_weapon_num_on_aircraft(unit_object, '中程') / 4  # 中层空空导弹数量
            XWS_missle_num = self.get_weapon_num_on_aircraft(unit_object, '响尾蛇') / 2  # 响尾蛇导弹数量

            feat_mine_air = [lat_relative, lon_relative, head, alt, speed, des_alt, des_speed, fuel_state,
                             extra_info_near_ally_aircraft_num, air2air_missle_num, XWS_missle_num]
        # ————————————————————————————————————————————————————————————————————————————————————————————
        enemy_info = []  # 敌方所有舰船信息和附近的4个敌方飞机信息
        enemy_info_air = [0] * 16
        if self.attack_airs_MiGe:
            enemy_airs = self.attack_airs_MiGe.copy()
            for i in range(4):
                con_air_mindistance, key = self.min_distance_to_enemy_air(air_strName, enemy_airs)
                if con_air_mindistance:
                    unit_object = con_air_mindistance
                    enemy_info_air[i * 4 + 0] = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                                self.max_lat - self.min_lat)
                    enemy_info_air[i * 4 + 1] = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                                self.max_lon - self.min_lon)
                    enemy_info_air[i * 4 + 2] = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / \
                                                self.air_att_max['F16']['max_speed']
                    enemy_info_air[i * 4 + 3] = unit_object.fCurrentHeading / self.air_att_max['F16']['max_head']
                    enemy_airs.pop(key)
                else:
                    pass
        enemy_info = enemy_info_air
        # ————————————————————————————————————————————————————————————————————————————————————————————
        ally_info = []  # 附近两架战斗机信息、预警机信息、最近干扰机信息
        ally_info_fighter = [0] * 14
        ally_info_E2K = [0] * 2
        ally_info_EC130H = [0] * 2
        fight_air = self.air_air_aircraft + self.anti_ships_aircraft
        ally_airs = {k: v for k, v in self.side.aircrafts.items() if
                     v.strName != air_strName and v.strName in fight_air}
        if ally_airs:
            for i in range(2):
                ally_air_mindistance, key = self.min_distance_to_enemy_air(air_strName, ally_airs)
                if ally_air_mindistance:
                    unit_object = ally_air_mindistance
                    ally_info_fighter[i * 7 + 0] = (unit_object.dLatitude - self.Map_center_point['latitude']) * 2 / (
                                self.max_lat - self.min_lat)
                    ally_info_fighter[i * 7 + 1] = (unit_object.dLongitude - self.Map_center_point['longitude']) * 2 / (
                                self.max_lon - self.min_lon)
                    ally_info_fighter[i * 7 + 2] = unit_object.fCurrentSpeed * self.NauticalMiles_to_Kilometers / \
                                                   self.air_att_max['F16']['max_speed']
                    ally_info_fighter[i * 7 + 3] = unit_object.fCurrentHeading / self.air_att_max['F16']['max_head']
                    ally_airs.pop(key)
                    if ally_air_mindistance.strName in self.air_air_aircraft:
                        ally_info_fighter[i * 7 + 4] = 1  # 表示飞机类型，空战飞机为1，反舰飞机为0
                        ally_info_fighter[i * 7 + 5] = self.get_weapon_num_on_aircraft(unit_object,
                                                                                       '中程') / 4  # 弹药量【AGM AIM】
                        ally_info_fighter[i * 7 + 6] = self.get_weapon_num_on_aircraft(unit_object, '响尾蛇') / 2
                    elif ally_air_mindistance.strName in self.anti_ships_aircraft:
                        ally_info_fighter[i * 7 + 4] = 0  # 表示飞机类型，空战飞机为1，反舰飞机为0
                        ally_info_fighter[i * 7 + 5] = self.get_weapon_num_on_aircraft(unit_object,
                                                                                       '中程') / 2  # 弹药量【AGM AIM】
                        ally_info_fighter[i * 7 + 6] = self.get_weapon_num_on_aircraft(unit_object, '鱼叉') / 2
                    else:
                        pass

        E2K_longitude, E2K_latitude = self.get_lon_lat_by_airName(self.E2K_aircraft[0])
        if E2K_longitude:
            ally_info_E2K[0] = (E2K_latitude - self.Map_center_point['latitude']) * 2 / (self.max_lat - self.min_lat)
            ally_info_E2K[1] = (E2K_longitude - self.Map_center_point['longitude']) * 2 / (self.max_lon - self.min_lon)

        EC130H_air_mindistance, key = self.min_distance_to_enemy_air(air_strName, self.EC130H_aircrafts_k_to_v)
        if EC130H_air_mindistance:
            ally_info_EC130H[0] = (EC130H_air_mindistance.dLatitude - self.Map_center_point['latitude']) * 2 / (
                        self.max_lat - self.min_lat)
            ally_info_EC130H[1] = (EC130H_air_mindistance.dLongitude - self.Map_center_point['longitude']) * 2 / (
                        self.max_lon - self.min_lon)

        ally_info = ally_info_fighter + ally_info_E2K + ally_info_EC130H

        extra_info = self.get_extra_info()  # 特征信息可以扩展的接口

        F16ATA_observation = feat_mine_air + enemy_info + ally_info + extra_info
        return F16ATA_observation

    def get_reward(self, group_name):
        # if self.operation_groups_status[group_name]:
        #     return 0
        if group_name in self.operation_groups_class.keys():
            unit = self.operation_groups_class[group_name]
            reward_distance_encourage = self.get_reward_distance(unit, group_name) / 500 / 10 * 2
        else:
            reward_distance_encourage = 0

        reward_score = (self._get_win_score() - self.last_score) / 300

        reward = reward_distance_encourage + reward_score
        return reward

    def get_reward_for_all_agents(self):
        self.agent_reward = {}
        for operation_group_name, agent_id in self.Agent_TO_Agent_IDs.items():
            if self.operation_groups_status[operation_group_name]:
                if not self.last_operation_groups_status[operation_group_name]:
                    self.agent_reward[agent_id] = 0
            else:
                self.agent_reward[agent_id] = self.get_reward(operation_group_name)

        return self.agent_reward

    def get_E2K_reward(self, strName):
        unit_object = [v for k, v in self.E2K_aircraft_k_to_v.items() if v.strName == strName][0]

        max_score = 1344
        reward_score = self.get_win_score() / max_score * 2  # 30/1344 = 0.022   1

        near_aircraft_num_400 = self.get_enemy_aircrafts_num_in_certain_distance(unit_object, 400 * 1000)
        near_ship_num_400 = self.get_enemy_ship_num_in_certain_distance(unit_object, 400 * 1000)
        reward_detection = (near_ship_num_400 + near_aircraft_num_400) / 17.0 / 10  # 0.1
        # if reward_detection:
        #     print('Debug')

        reward_damaged_aircrafts = self.reward_of_aircraft_damage(unit_object) / 100 / 100  # 0.01

        E2K_coefficient = 0.5
        EC130H_coefficient = 0.5
        F16ATA_coefficient = 1.0
        F16ATS_coefficient = 0.8
        reward_distance_encourage = self.get_reward_distance(unit_object) * E2K_coefficient / 30  # 0.01

        reward = reward_score + reward_detection + reward_damaged_aircrafts + reward_distance_encourage
        return reward

    def reward_of_damaged_aircraft(self):
        reward_damaged_aircrafts = 0
        for aircraft_GUID, aircraft in self.side.aircrafts.items():
            if int(aircraft.strDamageState) < 20:
                pass
            elif 20 <= int(aircraft.strDamageState) < 50:
                reward_damaged_aircrafts = reward_damaged_aircrafts - 5
            elif int(aircraft.strDamageState) >= 50 and int(aircraft.strDamageState) < 80:
                reward_damaged_aircrafts = reward_damaged_aircrafts - 10
            else:
                reward_damaged_aircrafts = reward_damaged_aircrafts - 15
        return reward_damaged_aircrafts

    def reward_of_aircraft_damage(self, unit_object):
        damage_reward = 0
        damage_status = int(unit_object.strDamageState)
        damage_reward = damage_status * (-1)
        if damage_status:
            print('Debug')
        return damage_reward

    def get_reward_distance(self, unit_object, group_name):
        lat_1 = unit_object.dLatitude
        long_1 = unit_object.dLongitude
        distance_last = self.agent_last_distance_to_target[self.Agent_TO_Agent_IDs[group_name]]
        distance_now = get_two_point_distance(long_1, lat_1, self.ship_HM['dLongitude'], self.ship_HM['dLatitude'])
        score_dis = distance_last - distance_now  # 贴近才有正奖励
        self.agent_last_distance_to_target[self.Agent_TO_Agent_IDs[group_name]] = distance_now

        if 'A2A' in group_name:
            distance_interval = 150000
        elif 'A2S' in group_name:
            distance_interval = 150000
        elif 'EJ' in group_name:
            distance_interval = 200000
        else:
            pass

        a = self.get_simu_time_degree()
        if a <= 0.5:
            time_coef = 0.5 - a
        else:
            time_coef = 0

        if distance_last < distance_interval and distance_now < distance_interval:
            return 0
        else:
            return (score_dis / 1000) * time_coef

    def get_EC130H_reward(self, strName):

        unit_object = [v for k, v in self.EC130H_aircrafts_k_to_v.items() if v.strName == strName][0]
        max_score = 1344
        reward_score = self.get_win_score() / max_score * 2  # 30/1344 = 0.022   1

        reward_damaged_aircrafts = self.reward_of_aircraft_damage(unit_object) / 100 / 100  # 0.01

        E2K_coefficient = 0.5
        EC130H_coefficient = 0.5
        F16ATA_coefficient = 1.0
        F16ATS_coefficient = 0.8
        reward_distance_encourage = self.get_reward_distance(unit_object) * EC130H_coefficient / 30  # 0.01

        reward = reward_score + reward_damaged_aircrafts + reward_distance_encourage
        return reward

    def get_F16ATA_reward(self, strName):
        unit_object = [v for k, v in self.F16ATA_aircrafts_k_to_v.items() if v.strName == strName][0]
        max_score = 1344
        reward_score = self.get_win_score() / max_score * 2  # 30/1344 = 0.022   1
        near_aircraft_num_400 = self.get_enemy_aircrafts_num_in_certain_distance(unit_object, 180 * 1000)
        reward_detection = (0 + near_aircraft_num_400) / 14.0 / 10  # 0.1


        reward_damaged_aircrafts = self.reward_of_aircraft_damage(unit_object) / 100 / 100  # 0.01

        E2K_coefficient = 0.5
        EC130H_coefficient = 0.5
        F16ATA_coefficient = 1.0
        F16ATS_coefficient = 0.8
        reward_distance_encourage = self.get_reward_distance(unit_object) * F16ATA_coefficient / 30  # 0.01

        reward = reward_score + reward_detection + reward_damaged_aircrafts + reward_distance_encourage
        return reward

    def get_F16ATS_reward(self, strName):
        unit_object = [v for k, v in self.F16ATS_aircrafts_k_to_v.items() if v.strName == strName][0]
        max_score = 1344
        reward_score = self.get_win_score() / max_score * 2  # 30/1344 = 0.022   1

        near_aircraft_num_400 = self.get_enemy_aircrafts_num_in_certain_distance(unit_object, 180 * 1000)
        near_ship_num_400 = self.get_enemy_ship_num_in_certain_distance(unit_object, 180 * 1000)
        reward_detection = (near_ship_num_400 + near_aircraft_num_400) / 17.0 / 10  # 0.1

        reward_damaged_aircrafts = self.reward_of_aircraft_damage(unit_object) / 100 / 100  # 0.01

        E2K_coefficient = 0.5
        EC130H_coefficient = 0.5
        F16ATA_coefficient = 1.0
        F16ATS_coefficient = 0.8
        reward_distance_encourage = self.get_reward_distance(unit_object) * F16ATS_coefficient / 30  # 0.01

        reward = reward_score + reward_detection + reward_damaged_aircrafts + reward_distance_encourage
        return reward

    def reward_of_missile_distance(self, unit_object):
        missile_reward = 0
        contacts = {k: v for k, v in self.side.contacts.items()}  # 探测到的敌方所有实体
        if contacts.__len__() > 0:
            contacts_list = []
            for k, v in contacts.items():
                if v.m_ContactType == 1:
                    lat_1 = unit_object.dLatitude
                    long_1 = unit_object.dLongitude
                    for m, n in contacts.items():
                        lat_2 = n.dLatitude
                        long_2 = n.dLongitude
                        distance = get_two_point_distance(long_1, lat_1, long_2, lat_2)
                        if distance < 50:
                            contacts_list.append((n, distance))
            if contacts_list.__len__() != 0:
                contacts_list.sort(key=lambda x: x[1], reverse=False)
                if contacts_list[0][1] < 10:
                    missile_reward = -100
                else:
                    missile_reward = -10 * ((contacts_list[0][1]) / 50)
        return missile_reward

    def set_dones_info_for_all_agents(self):
        done = {agent_id: True for agent_id in self.agent_IDs}
        end = self._is_done()
        if end:
            done['__all__'] = True
        else:
            done['__all__'] = False
        done_flag = 0
        for group_name, status in self.operation_groups_status.items():
            agent_id = self.Agent_TO_Agent_IDs[group_name]
            if end:
                done[agent_id] = True
            else:
                if status == False:
                    done[agent_id] = False
                else:
                    done[agent_id] = True
                    done_flag += 1
        if done_flag == 4: done['__all__'] = True
        return done

    def get_E2K_done(self, strName):
        unit_object = [v for k, v in self.E2K_aircraft_k_to_v.items() if v.strName == strName][0]
        if unit_object:
            if unit_object.strDamageState == '100' or unit_object.strAirOpsConditionString != 0:
                self.aircrafts_controll_status[self.Agent_TO_Agent_IDs[strName]] = False
                return True
            else:
                return False

    def get_EC130H_done(self, strName):
        unit_object = [v for k, v in self.EC130H_aircrafts_k_to_v.items() if v.strName == strName][0]
        if unit_object:
            if unit_object.strDamageState == '100' or unit_object.strAirOpsConditionString != 0:
                self.aircrafts_controll_status[self.Agent_TO_Agent_IDs[strName]] = False
                return True
            else:
                return False

    def get_F16ATA_done(self, strName):
        unit_object = [v for k, v in self.F16ATA_aircrafts_k_to_v.items() if v.strName == strName][0]
        if unit_object:
            if unit_object.strDamageState == '100' or unit_object.strAirOpsConditionString != 0:
                self.aircrafts_controll_status[self.Agent_TO_Agent_IDs[strName]] = False
                return True
            else:
                return False

    def get_F16ATS_done(self, strName):
        unit_object = [v for k, v in self.F16ATS_aircrafts_k_to_v.items() if v.strName == strName][0]
        if unit_object:
            if unit_object.strDamageState == '100' or unit_object.strAirOpsConditionString != 0:
                self.aircrafts_controll_status[self.Agent_TO_Agent_IDs[strName]] = False
                return True
            else:
                return False

    def generate_agents_infos(self):
        multi_agent_infos = {}
        return multi_agent_infos

    def judge_end_of_one_episode(self):
        done = False
        response_dic = self.scenario.get_responses()
        for _, v in response_dic.items():
            if v.Type == 'EndOfDeduction':
                print('打印出标记：EndOfDeduction')
                done = True
        return done

    def assign_action_for_agents(self, action_dict):
        for agent_id, action_tuple in action_dict.items():
            group_name = self.Agent_IDs_TO_Agent[agent_id]
            if self.operation_groups_status[group_name]:
                continue
            if 'A2A' in group_name:
                self.excute_F16_ATA(group_name, action_tuple)
            elif 'A2S' in group_name:
                self.excute_F16_ATS(group_name, action_tuple)
            else:
                print(group_name, '不需要执行动作')

    def excute_F16_ATA(self, group_name, action_tuple):
        radar_status, move_action = action_tuple
        throttle, head = self.Analytic_Move_Parameters(group_name, move_action)

        units = self.operation_groups[group_name]
        for unit_name, unit_class in units.items():
            doctrine = unit_class.get_doctrine()
            if doctrine:
                if radar_status:
                    doctrine.set_em_control_status(em_item='Radar', status='Active')
                else:
                    doctrine.set_em_control_status(em_item='Radar', status='Passive')
                doctrine.set_em_control_status(em_item='OECM', status='Active')

                unit_class.set_throttle(throttle)
                new_point = get_point_with_point_bearing_distance(unit_class.dLatitude, unit_class.dLongitude,
                                                                  head, distance=10)
                unit_class.delete_coursed_point(point_index=1, clear=True)
                unit_class.plot_course([(new_point['latitude'], new_point['longitude'])])

    def excute_F16_ATS(self, group_name, action_tuple):
        radar_status, move_action = action_tuple
        throttle, head = self.Analytic_Move_Parameters(group_name, move_action)

        units = self.operation_groups[group_name]
        for unit_name, unit_class in units.items():
            doctrine = unit_class.get_doctrine()
            if doctrine:
                if radar_status:
                    doctrine.set_em_control_status(em_item='Radar', status='Active')
                else:
                    doctrine.set_em_control_status(em_item='Radar', status='Passive')
                doctrine.set_em_control_status(em_item='OECM', status='Active')  # ??

                unit_class.set_throttle(throttle)
                new_point = get_point_with_point_bearing_distance(unit_class.dLatitude, unit_class.dLongitude,
                                                                  head, distance=10)
                unit_class.delete_coursed_point(point_index=1, clear=True)
                unit_class.plot_course([(new_point['latitude'], new_point['longitude'])])

    def excute_EC(self, group_name, action_tuple):
        move_action = action_tuple[0]
        throttle, head = self.Analytic_Move_Parameters(group_name, move_action)
        speed_choice = [333.36, 370.4, 425.96]
        units = self.operation_groups[group_name]
        for unit_name, unit_class in units.items():
            doctrine = unit_class.get_doctrine()
            if doctrine:
                doctrine.set_em_control_status(em_item='Radar', status='Active')
                doctrine.set_em_control_status(em_item='OECM', status='Active')  # ??
                unit_class.set_throttle(throttle)
                # distance_D = speed_choice[throttle - 1] / 3600 * (etc.DURATION_INTERVAL)
                new_point = get_point_with_point_bearing_distance(unit_class.dLatitude, unit_class.dLongitude,
                                                                  head, distance=10)
                unit_class.delete_coursed_point(point_index=1, clear=True)
                unit_class.plot_course([(new_point['latitude'], new_point['longitude'])])

    def excute_aircraft_agent_action_E2K(self, unit_object, action):

        move_choice = [i + 1 for i in range(3 * 6)]
        speed_choice = [370.4, 592.64, 620.42]
        move_action = action[0]

        throttle, head = self.Analytic_Move_Parameters(unit_object, move_action)
        unit_air = unit_object

        unit_air.set_throttle(throttle)
        distance_D = speed_choice[throttle - 1] / 3600 * (etc.DURATION_INTERVAL)
        new_point = get_point_with_point_bearing_distance(unit_air.dLatitude, unit_air.dLongitude,
                                                          head, distance=10)
        unit_air.delete_coursed_point(clear=True)
        unit_air.plot_course([(new_point['latitude'], new_point['longitude'])])

    def Analytic_Move_Parameters(self, group_name, action):
        if 'A2' in group_name:
            throttle_choice = [1, 2, 3, 4]
        else:
            throttle_choice = [1, 2, 3]
        head_choice = [i * 60 for i in range(6)]
        action = action + 1
        if 1 <= action <= 6:
            throttle = throttle_choice[0]
            action = action - 0
            head = head_choice[action - 1]
        elif 7 <= action <= 12:
            throttle = throttle_choice[1]
            action = action - 6
            head = head_choice[action - 1]
        elif 13 <= action <= 18:
            throttle = throttle_choice[2]
            action = action - 12
            head = head_choice[action - 1]
        elif 19 <= action <= 24:
            throttle = throttle_choice[3]
            action = action - 18
            head = head_choice[action - 1]

        return throttle, head

    def excute_aircraft_agent_action_EC130H(self, unit_object, action):
        move_choice = [i + 1 for i in range(3 * 6)]
        speed_choice = [333.36, 370.4, 425.96]
        move_action = action[0]

        throttle, head = self.Analytic_Move_Parameters(unit_object, move_action)
        unit_air = unit_object

        unit_air.set_throttle(throttle)
        distance_D = speed_choice[throttle - 1] / 3600 * (etc.DURATION_INTERVAL)
        new_point = get_point_with_point_bearing_distance(unit_air.dLatitude, unit_air.dLongitude,
                                                          head, distance=10)
        unit_air.delete_coursed_point(clear=True)
        unit_air.plot_course([(new_point['latitude'], new_point['longitude'])])

    def excute_aircraft_agent_action_F16ATA(self, unit_object, action):
        move_choice = [i + 1 for i in range(4 * 6)]
        speed_choice = [648.2, 888.96, 1055.64, 1259.36]
        move_action = action[0]

        throttle, head = self.Analytic_Move_Parameters(unit_object, move_action)
        unit_air = unit_object

        unit_air.set_throttle(throttle)
        distance_D = speed_choice[throttle - 1] / 3600 * (etc.DURATION_INTERVAL)
        new_point = get_point_with_point_bearing_distance(unit_air.dLatitude, unit_air.dLongitude,
                                                          head, distance=10)
        unit_air.delete_coursed_point(clear=True)
        unit_air.plot_course([(new_point['latitude'], new_point['longitude'])])
        # ________________________________________________________________________________________________________
        attack_dis_choice = [35, 40, 45, 50]
        attack_dis = attack_dis_choice[action[1]]
        qishe = 2
        self.set_air_weapon_doctrine_wp718(unit_air, qishe, attack_dis)
        # ________________________________________________________________________________________________________

        doctrine = unit_object.get_doctrine()
        Radar_state = action[2]
        if Radar_state == 1:
            doctrine.set_em_control_status('Radar', 'Active')
        else:
            doctrine.set_em_control_status('Radar', 'Passive')

    def set_air_weapon_doctrine_wp718(self, unit_air, attack_dis):
        aircraft_doctrine = unit_air.get_doctrine()
        if aircraft_doctrine:
            aircraft_doctrine.set_weapon_release_authority('718', '2100', quantity_salvo=1, shooter_salvo='inherit',
                                                           firing_range='max', self_defense='max',
                                                           escort='')  # 打直升机单发齐射
            aircraft_doctrine.set_weapon_release_authority('718', '2000', quantity_salvo=2, shooter_salvo='inherit',
                                                           firing_range=attack_dis, self_defense='max',
                                                           escort='')  # 打敌方飞机时2发齐射 Aircraft_Unspecified = 2000
            aircraft_doctrine.set_weapon_release_authority('718', '2002', quantity_salvo=2, shooter_salvo='inherit',
                                                           firing_range=attack_dis, self_defense='max',
                                                           escort='')  # 打敌方飞机时2发齐射 Aircraft_4th_Generation = 2002
        else:
            print("there is not aircraft_doctrine!!")

    def set_air_weapon_doctrine_wp816(self, unit_air, attack_dis):
        aircraft_doctrine = unit_air.get_doctrine()
        if aircraft_doctrine:
            aircraft_doctrine.set_weapon_release_authority(weapon_dbid='816', target_type='2999',
                                                           quantity_salvo=2, shooter_salvo='max',
                                                           firing_range=attack_dis, self_defense='max', escort='')
            aircraft_doctrine.set_weapon_release_authority(weapon_dbid='816', target_type='3000',
                                                           quantity_salvo=2, shooter_salvo='max',
                                                           firing_range=attack_dis, self_defense='max', escort='')
        else:
            print("there is not aircraft_doctrine!!")

    def get_win_score(self):
        if self.steps % 100 == 0:
            # print(f'step: {self.steps} buleside total score is {self.side.iTotalScore}')
            pass
        return float(self.side.iTotalScore)

    def excute_aircraft_agent_action_F16ATS(self, unit_object, action):
        move_choice = [i + 1 for i in range(4 * 6)]
        speed_choice = [648.2, 888.96, 1055.64, 1259.36]
        move_action = action[0]

        throttle, head = self.Analytic_Move_Parameters(unit_object, move_action)
        unit_air = unit_object

        unit_air.set_throttle(throttle)
        distance_D = speed_choice[throttle - 1] / 3600 * (etc.DURATION_INTERVAL)
        new_point = get_point_with_point_bearing_distance(unit_air.dLatitude, unit_air.dLongitude,
                                                          head, distance=10)
        unit_air.delete_coursed_point(clear=True)
        unit_air.plot_course([(new_point['latitude'], new_point['longitude'])])

        attack_dis_choice = [35, 40, 45, 50]
        attack_dis = attack_dis_choice[action[1]]
        qishe = 2
        self.set_air_weapon_doctrine_wp718(unit_air, qishe, attack_dis)

        attack_dis_choice_01 = [40, 50, 60, 70]
        attack_dis_01 = attack_dis_choice_01[action[2]]
        qishe = 2
        self.set_air_weapon_doctrine_wp816(unit_air, qishe, attack_dis_01)

        doctrine = unit_object.get_doctrine()
        Radar_state = action[3]
        if Radar_state == 1:
            doctrine.set_em_control_status('Radar', 'Active')
        else:
            doctrine.set_em_control_status('Radar', 'Passive')

    def reward_of_aircraft_damage(self, unit_object):
        damage_reward = 0
        damage_status = int(unit_object.strDamageState)
        damage_reward = damage_status * (-1)
        return damage_reward

    def reward_of_contacts_num_founded_by_E2K(self):
        num = 0
        contacts = {k: v for k, v in self.side.contacts.items()}  # 探测到的敌方所有实体
        for k, v in contacts.items():
            if v.m_ContactType == 0 or v.m_ContactType == 2:
                if 'E-2K' in v.m_DetectionRecord:
                    num += 1
        num_reward = num * 50
        return num_reward

    def _get_win_score(self):

        return float(self.side.iTotalScore)

    def set_combat_area_ref_point(self):
        count = 0
        ref_point = {}

        count = count + 1
        point_name = 'point_%d' % count
        ref_point[point_name] = [self.max_lat, self.min_lon]
        count = count + 1
        point_name = 'point_%d' % count
        ref_point[point_name] = [self.max_lat, self.max_lon]
        count = count + 1
        point_name = 'point_%d' % count
        ref_point[point_name] = [self.min_lat, self.max_lon]
        count = count + 1
        point_name = 'point_%d' % count
        ref_point[point_name] = [self.min_lat, self.min_lon]
        count = count + 1
        point_name = 'point_%d' % count
        ref_point[point_name] = [self.Map_center_point['latitude'], self.Map_center_point['longitude']]
        self.set_ref_points(ref_point)
        self.env.step()
        self.side.add_mission_patrol(name='战斗区域示意图', patrol_type_num=0,
                                     zone_points=['point_1', 'point_2', 'point_3', 'point_4'])
        self.ref_point = ref_point

    def set_combat_area(self):
        count = 0
        area_dict = {}
        for i in range(64):
            if (i + 1) % 8 != 0 and i < 56:
                count = count + 1
                area_name = 'area_%d' % count
                ref_point_list = ['point_%d' % (i + 1), 'point_%d' % (i + 1 + 1), 'point_%d' % (i + 1 + 9),
                                  'point_%d' % (i + 1 + 8)]
                area_dict[area_name] = ref_point_list
                self.side.add_zone(1, area_name, ref_point_list, ['Ship', 'Aircraft'], 'true', 'Hostile')
                # self.env.step()
        self.combat_area = area_dict

    def set_ref_points(self, points_dict):
        for point_name, p in points_dict.items():
            cmd = "ScenEdit_AddReferencePoint({" + \
                  f"side='{self.side_name}', name='{point_name}', lat={p[0]}, lon={p[1]}" + "})"
            self.scenario.mozi_server.send_and_recv(cmd)

    def _get_initial_state(self):
        self.reset_nums += 1
        if self.env_config['mode'] in ['train', 'development']:
            if self.reset_nums % 5 == 0:
                docker_ip_port = self.ip_port
                for _ in range(MAX_DOCKER_RETRIES):

                    try:
                        if self.env_config['mode'] == 'train':
                            restart_container(self.schedule_addr,
                                              self.schedule_port,
                                              self.env_config['training_id'],
                                              docker_ip_port)
                        else:
                            restart_mozi_container(docker_ip_port)
                        self.env = Environment(etc.SERVER_IP,
                                               etc.SERVER_PORT,
                                               'linux',
                                               etc.SCENARIO_NAME,
                                               etc.SIMULATE_COMPRESSION,
                                               etc.DURATION_INTERVAL,
                                               etc.SYNCHRONOUS)
                        self.env.start(self.ip, self.port)
                        break
                    except Exception:
                        print(f"{time.strftime('%H:%M:%S')} 在第{self.steps}步，第{_}次重启docker失败！！！")
                        continue
                self.scenario = self.env.reset(self.side_name)
            else:
                self.scenario = self.env.reset(self.side_name)
        else:
            self.scenario = self.env.reset(self.side_name)

    def get_aircraft_self_info(self, our_unit_name):
        aircraft_state = []
        unit_object = None
        for unit_Guid, unit in self.side.aircrafts.items():
            if unit.strName == our_unit_name:
                unit_object = unit
        aircraft_state.append(unit_object.dLatitude)  # 飞机维度,float
        aircraft_state.append(unit_object.dLongitude)  # 飞机经度,float
        aircraft_state.append(unit_object.fCurrentHeading)  # 飞机朝向,float
        aircraft_state.append(unit_object.fCurrentSpeed)  # 飞机速度,float
        aircraft_state.append(unit_object.fCurrentAlt)  # 飞机高度,float
        aircraft_state.append(unit_object.fDesiredAlt)  # 期望维度,float
        aircraft_state.append(unit_object.iCurrentFuelQuantity)  # 当前油量,float
        aircraft_state.append(unit_object.m_CurrentThrottle)  # 油门状态,int
        aircraft_state.append(int(unit_object.strDamageState))  # 毁伤状态，int
        aircraft_state_type = unit_object.get_status_type()
        aircraft_status = 0
        if aircraft_state_type == 'validToFly':  # 在基地可马上部署飞行任务
            aircraft_status = 1
        elif aircraft_state_type == 'InAir':  # 在空中可部署巡逻、进攻、航路规划
            aircraft_status = 2
        elif aircraft_state_type == 'InAirRTB':  # 在空中返航或降落
            aircraft_status = 3
        aircraft_state.append(aircraft_status)  # 飞机状态，int
        if unit_object.m_UnitWeapons.find('反舰') > 0:
            extra_info = self.get_aircraft_extra_info_for_air2ship(our_unit_name)  # dim=4
            aircraft_state = aircraft_state + extra_info
        elif 'E-2K' in unit_object.strName:
            extra_info = self.get_aircraft_extra_info_for_E2k(our_unit_name)  # dim=1
            aircraft_state = aircraft_state + extra_info + [0, 0, 0]  # 补0
        elif 'EC' in unit_object.strName:
            extra_info = self.get_aircraft_extra_info_for_EC(our_unit_name)  # dim=1
            aircraft_state = aircraft_state + extra_info + [0, 0, 0]  # 补0
        else:
            extra_info = self.get_aircraft_extra_info_for_air2air(our_unit_name)  # dim=3
            aircraft_state = aircraft_state + extra_info + [0]  # 为了使其与反舰飞机的维度相同，补一个0项
        return aircraft_state

    def get_battlefield_time(self):
        t = 0
        try:
            tx = self.scenario.m_Time
            t = int(tx)
        except:
            traceback.print_exc()
        return time.strftime('%Y-%m-%d-%H:%M:%S', time.localtime(t)), t

    def set_E2K_Patrol_mission(self):
        index = 0
        ref_points_names = []
        points = [('122.52.30', '18.32.12'), ('123.53.27', '18.31.8'), ('123.52.59', '17.42.41'),
                  ('122.51.49', '17.50.5')]
        for num in range(4):
            ref_points_names.append('YJ-' + str(num + 1))
        for point in points:
            x, y = convert_gps_into_float_xy(point[1], point[0])
            self.side.add_reference_point(ref_points_names[index], x, y)
            index = index + 1
        patrol_mission_E2K = self.side.add_mission_patrol('预警机巡逻', 0, ref_points_names)
        patrol_mission_E2K.area = 0
        E2K_guid = self.aircraft_guid_set[20]
        patrol_mission_E2K.strName = '预警机巡逻'
        patrol_mission_E2K.assign_unit(E2K_guid, 'false')
        patrol_mission_E2K.set_one_third_rule('false')
        patrol_mission_E2K.set_start_time('2021-05-26 11:56:00')
        patrol_mission_E2K.set_end_time('2021-05-26 14:55:43')
        patrol_mission_E2K.set_is_active('true')  # 启动任务
        patrol_mission_E2K.set_throttle('transitThrottleAircraft', 'Cruise')  # 设置出航油门为巡航
        patrol_mission_E2K.set_throttle('stationThrottleAircraft', 'Loiter')  # 设置阵位油门为低速
        patrol_mission_E2K.set_opa_check('false')  # 不对巡逻区外的目标进行分析
        patrol_mission_E2K.set_emcon_usage('true')  # 只在巡逻区开启雷达
        patrol_mission_E2K.set_wwr_check('false')  # 不对武器射程内的目标进行分析
        patrol_mission_E2K.set_flight_size(1)  # 单机编队
        patrol_mission_E2K.set_flight_size_check('false')  # 飞机数不够编队规模也能起飞

    def _get_env(self):
        if self.env_config['mode'] == 'train':
            self.schedule_addr = self.env_config['schedule_addr']
            self.schedule_port = self.env_config['schedule_port']
            scenario_name = etc.SCENARIO_NAME
            platform = 'linux'
            self._create_env(platform, scenario_name=scenario_name)
        elif self.env_config['mode'] == 'development':
            scenario_name = etc.SCENARIO_NAME
            platform = 'linux'
            self._create_env(platform, scenario_name=scenario_name)
        elif self.env_config['mode'] == 'versus':
            scenario_name = etc.SCENARIO_NAME
            platform = 'linux'
            self._create_env(platform, scenario_name=scenario_name)
        elif self.env_config['mode'] == 'eval':
            scenario_name = etc.EVAL_SCENARIO_NAME
            platform = 'windows'
            self._create_env(platform, scenario_name=scenario_name)

        else:
            raise NotImplementedError

    @staticmethod
    def _get_unit_weapon(unit):
        weapon = list(map(lambda x: x.split('$'), unit.m_UnitWeapons.split('@')))
        weapon_list = list(map(lambda x, y: x + [y[-1]], list(map(lambda x: x[0].split('x '), weapon)), weapon))
        return weapon_list

    def _create_env(self, platform, scenario_name=None):
        for _ in range(MAX_DOCKER_RETRIES):
            try:
                self.env = Environment(etc.SERVER_IP,
                                       etc.SERVER_PORT,
                                       platform,
                                       scenario_name,
                                       etc.SIMULATE_COMPRESSION,
                                       etc.DURATION_INTERVAL,
                                       etc.SYNCHRONOUS)
                if self.env_config['avail_docker_ip_port']:
                    self.avail_ip_port_list = self.env_config['avail_docker_ip_port']
                    print('avail_docker_ip_port:')
                    print(self.avail_ip_port_list)
                else:
                    raise Exception('no avail port!')
                self.ip_port = self.avail_ip_port_list[0]

                self.ip = self.avail_ip_port_list[0][0]
                self.port = self.avail_ip_port_list[0][1]

                self.ip_port = f'{self.ip}:{self.port}'
                print('self.ip_port:' + self.ip_port)
                self.env.start(self.ip, self.port)  #
                break
            except Exception:
                continue

    def create_operation_groups(self):
        AIR_TO_AIR_GROUPS = ['A2A_GROUP_1', 'A2A_GROUP_2']
        AIR_TO_SURFACE_GROUPS = ['A2S_GROUP_1', 'A2S_GROUP_2']

        for k, v in self.side.aircrafts.items():
            if v.strName in self.air_air_aircraft_Group01:
                self.air_to_air_groups[AIR_TO_AIR_GROUPS[0]][k] = v
            if v.strName in self.air_air_aircraft_Group02:
                self.air_to_air_groups[AIR_TO_AIR_GROUPS[1]][k] = v
            if v.strName in self.anti_ships_aircraft_Group01:
                self.air_to_surface_groups[AIR_TO_SURFACE_GROUPS[0]][k] = v
            if v.strName in self.anti_ships_aircraft_Group02:
                self.air_to_surface_groups[AIR_TO_SURFACE_GROUPS[1]][k] = v

    def operation_group_out(self):
        for k, v in self.air_to_air_groups.items():
            cmd = "Hs_ScenEdit_AirOpsGroupOut(" + "{"
            for guid in v.keys():
                cmd += f"\'{guid}\'" + ","
            cmd += "})"  # 按编队出动
            self.scenario.mozi_server.send_and_recv(cmd)

        for k, v in self.air_to_surface_groups.items():
            cmd = "Hs_ScenEdit_AirOpsGroupOut(" + "{"
            for guid in v.keys():
                cmd += f"\'{guid}\'" + ","
            cmd += "})"  # 按编队出动
            self.scenario.mozi_server.send_and_recv(cmd)

    def operation_group_name_to_guid(self):
        for k, v in self.side.groups.items():
            for group_name, units in self.air_to_air_groups.items():
                if v.m_GroupLead in units.keys():
                    self.air_to_air_group_name_to_guid[group_name] = k
                    # 编对命名
                    cmd = "ScenEdit_SetUnit({" + f"Unitname='{v.strName}'," + f"Newname='{group_name}'" + "})"
                    self.scenario.mozi_server.send_and_recv(cmd)
                    break
            for group_name, units in self.air_to_surface_groups.items():
                if v.m_GroupLead in units.keys():
                    self.air_to_surface_group_name_to_guid[group_name] = k
                    # 编对命名
                    cmd = "ScenEdit_SetUnit({" + f"Unitname='{v.strName}'," + f"Newname='{group_name}'" + "})"
                    self.scenario.mozi_server.send_and_recv(cmd)
                    break

        self.operation_groups = dict(self.air_to_air_groups,
                                     **self.air_to_surface_groups)
        self.operation_groups_name_to_guid = dict(self.air_to_air_group_name_to_guid,
                                                  **self.air_to_surface_group_name_to_guid)
        assert self.operation_groups.keys() == self.operation_groups_name_to_guid.keys()

    def control_operation_group(self, group_name, altitude, speed=None, heading=None):
        if altitude == 0:  # 保持高度
            altitude = 10000
            cmd = "ScenEdit_SetUnit({Unitname=" + f"\'{group_name}\'," + f"Altitude={altitude}" + "})"
            self.scenario.mozi_server.send_and_recv(cmd)
        elif altitude == 1:  # 降低高度
            altitude = 10000
            cmd = "ScenEdit_SetUnit({Unitname=" + f"\'{group_name}\'," + f"Altitude={altitude}" + "})"
            self.scenario.mozi_server.send_and_recv(cmd)
        else:  # 提升高度
            altitude = 20000
            cmd = "ScenEdit_SetUnit({Unitname=" + f"\'{group_name}\'," + f"Altitude={altitude}" + "})"
            self.scenario.mozi_server.send_and_recv(cmd)

    def control_operation_group_next_point(self, group_name, lat, lon):
        cmd = "ScenEdit_SetUnit({Unitname=" + f"\'{group_name}\'," + "course={{" \
              + f"latitude=\'{lat}\'," + f"longitude=\'{lon}\'" + "}}})"
        ret = self.scenario.mozi_server.send_and_recv(cmd)

    def _update_operation_groups_status(self):
        for group_name, units in self.operation_groups.items():
            if units:
                if self.operation_groups_status[group_name]:
                    continue
                temp_status = []
                for unit_guid, unit_class in units.items():
                    if int(unit_class.strAirOpsConditionString) is 11:
                        temp_status.append(True)
                    else:
                        if unit_class.get_doctrine() == None:
                            temp_status.append(True)
                        else:
                            temp_status.append(False)
                self.operation_groups_status[group_name] = all(temp_status)
            else:
                self.operation_groups_status[group_name] = True

    def update_ships_no_nav_area(self):
        for ship_name, ship_value in self.attack_ships.items():
            if ship_name in self.no_nav_area_info:
                self.update_ref_points(ship_name, ship_value.dLatitude, ship_value.dLongitude)
            else:
                self.set_no_nav_area(ship_name, ship_value.dLatitude, ship_value.dLongitude)
        points_name = []
        areas_name = []
        for area_name in self.no_nav_area_info.keys():
            if area_name not in self.attack_ships:
                areas_name.append(area_name)
        for area_name in areas_name:
            points_dict = self.no_nav_area_info.pop(area_name)
            points_name += points_dict.keys()

        if points_name:
            self.delete_ref_points(points_name)

    def delete_ref_points(self, points_name):
        for point_name in points_name:
            cmd = "ScenEdit_DeleteReferencePoint({" + f"side='{self.side_name}',name='{point_name}'" + "})"
            self.scenario.mozi_server.send_and_recv(cmd)

    def update_ref_points(self, area_name, latitude, longitude, outer_distance=20, inner_distance=19.8):
        bearing_list = [i for i in range(0, 361, 30)]  # 331
        # outer_distance = 20  # 海里
        point_name_prefix = f"{area_name}-outer-no-nav-"
        outer_points_dict = self.get_bear_point(point_name_prefix, latitude, longitude, bearing_list, outer_distance)
        self.no_nav_area_info[area_name].update(outer_points_dict)

        bearing_list = [i for i in range(360, -1, -30)]
        point_name_prefix = f"{area_name}-inner-no-nav-"
        inner_points_dict = self.get_bear_point(point_name_prefix, latitude, longitude, bearing_list, inner_distance)
        self.no_nav_area_info[area_name].update(inner_points_dict)

        for point_name, point in self.no_nav_area_info[area_name].items():
            cmd = "ScenEdit_SetReferencePoint({" + \
                  f"side='{self.side_name}',name='{point_name}', lat={point[0]}, lon={point[1]}" + "})"
            self.scenario.mozi_server.send_and_recv(cmd)

    def get_bear_point(self, point_name_prefix, lat, lon, bearing_list, distance):
        points_dict = {}
        for i, bearing in enumerate(bearing_list):
            point_name = point_name_prefix + f"{i}"
            cmd = "print(World_GetPointFromBearing({" + \
                  f"latitude=\'{lat}\', longitude=\'{lon}\', BEARING=\'{bearing}\', DISTANCE=\'{distance}\'" + "}))"
            ret = self.scenario.mozi_server.send_and_recv(cmd)
            ret = convert_lua_obj_to_dict(ret)
            point = (ret['Latitude'], ret['Longitude'])
            points_dict[point_name] = point
        return points_dict

    def set_no_nav_area(self, area_center_name, center_lat, center_lon, outer_distance=20, inner_distance=19.8):
        # 正北方向为0度，顺时针方向每30度设置一个参考点
        bearing_list = [i for i in range(0, 361, 30)]  # 331
        # outer_distance = 20  # 海里
        point_name_prefix = f"{area_center_name}-outer-no-nav-"
        outer_points_dict = self.get_bear_point(point_name_prefix, center_lat, center_lon, bearing_list, outer_distance)
        self.no_nav_area_info[area_center_name] = outer_points_dict
        self.set_ref_points(outer_points_dict)

        # 正北方向为0度，逆时针方向每30度设置一个参考点
        bearing_list = [i for i in range(360, -1, -30)]
        # inner_distance = 10  # 海里
        point_name_prefix = f"{area_center_name}-inner-no-nav-"
        inner_points_dict = self.get_bear_point(point_name_prefix, center_lat, center_lon, bearing_list, inner_distance)
        self.no_nav_area_info[area_center_name].update(inner_points_dict)
        self.set_ref_points(inner_points_dict)

        if not self.no_nav_area_info[area_center_name]:
            raise ValueError

        cmd = f"ScenEdit_AddZone('{self.side_name}', 0, " + \
              "{" + f"description='{area_center_name}', " + "Isactive=true, Affects={'Aircraft'}, Area={"
        for point_name in self.no_nav_area_info[area_center_name].keys():
            cmd += f"'{point_name}', "
        cmd = cmd + "}})"
        self.scenario.mozi_server.send_and_recv(cmd)

    def return_to_base(self):
        a2s_status = []
        for name, status in self.operation_groups_status.items():
            if 'A2S' in name:
                a2s_status.append(status)
        if all(a2s_status):
            for _, unit_class in self.agent_operation_units.items():
                unit_class.return_to_base()
            for _, unit_class in self.rule_operation_units.items():
                unit_class.return_to_base()